Gerenciamento de Formulários no Angular: Abordagens Baseadas em Templates e Reativas

O processamento de entradas de usuário é um pilar fundamental em aplicações Web. No Angular, existem duas estratégias principais para lidar com formulários: Template-driven (Baseado em Templates) e Reactive Forms (Formulários Reativos). Enquanto a primeira foca na simplicidade e no uso de diretivas diretamente no HTML, a segunda oferece controle total via código TypeScript, sendo ideal para cenários complexos.

  1. Formulários Basedaos em Templates (Template-driven)

Esta abordagem utiliza diretivas internas do Angular, como o ngModel, para gerenciar o estado do formulário diretamente no HTML. É a escolha ideal para formulários simples com regras de validação básicas.

Implementação e Vinculação de Dados

Para utilizar esta abordagem, é necessário importar o FormsModule no componente ou módulo correspondente.


import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-registro-simples',
  standalone: true,
  imports: [FormsModule],
  template: `
    <form #formRef="ngForm" (ngSubmit)="processarEnvio(formRef.value)">
      <div>
        <label>Nome do Usuário:</label>
        <input 
          type="text" 
          name="apelido" 
          ngModel 
          required 
          #campoApelido="ngModel"
        >
        @if (campoApelido.invalid && campoApelido.touched) {
          <small class="text-danger">O nome é obrigatório.</small>
        }
      </div>

      <div>
        <label>E-mail:</label>
        <input 
          type="email" 
          name="contatoEmail" 
          ngModel 
          required 
          email
          #campoEmail="ngModel"
        >
        @if (campoEmail.invalid && campoEmail.touched) {
          @if (campoEmail.errors?.['required']) {
            <small class="text-danger">E-mail é indispensável.</small>
          }
          @if (campoEmail.errors?.['email']) {
            <small class="text-danger">Formato de e-mail inválido.</small>
          }
        }
      </div>

      <button type="submit" [disabled]="formRef.invalid">Enviar Cadastro</button>
    </form>
  `
})
export class RegistroSimplesComponent {
  processarEnvio(dados: any) {
    console.log('Dados recebidos:', dados);
  }
}

Estados de Validação

O Angular rastreia automaticamente o estado de cada campo através de classes CSS e propriedades booleanas:

  • valid / invalid: Indica se a validação passou.
  • touched / untouched: Indica se o usuário interagiu e saiu do campo.
  • dirty / pristine: Indica se o valor original foi alterado.
  1. Formulários Reativos (Reactive Forms)

Os formulários reativos são baseados em fluxos de dados observáveis. A lógica reside inteiramente na classe TypeScript, facilitando testes unitários e manipulação dinâmica de campos.

Configuração com FormGroup e FormControl

Requer o ReactiveFormsModule para funcionar.


import { Component } from '@angular/core';
import { ReactiveFormsModule, FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
  selector: 'app-formulario-reativo',
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <form [formGroup]="authForm" (ngSubmit)="confirmar()">
      <div>
        <label>Identificador:</label>
        <input formControlName="login">
        @if (authForm.get('login')?.invalid && authForm.get('login')?.touched) {
          <span>Campo obrigatório</span>
        }
      </div>

      <div>
        <label>Senha:</label>
        <input type="password" formControlName="senha">
        @if (authForm.get('senha')?.errors?.['minlength']) {
          <span>Mínimo de 8 caracteres</span>
        }
      </div>

      <button type="submit" [disabled]="authForm.invalid">Entrar</button>
    </form>
  `
})
export class FormularioReativoComponent {
  authForm = new FormGroup({
    login: new FormControl('', [Validators.required]),
    senha: new FormControl('', [Validators.required, Validators.minLength(8)])
  });

  confirmar() {
    if (this.authForm.valid) {
      console.log('Payload:', this.authForm.value);
    }
  }
}

Integração com Signals (Angular v17+)

Com a introdução de Signals, podemos converter mudanças de estado do formulário em sinais reativos para otimizar a interface.


import { Component, computed } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { FormControl, ReactiveFormsModule } from '@angular/forms';

@Component({
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <input [formControl]="buscaControl" placeholder="Filtrar...">
    <p>Termo atual: {{ termoBusca() }}</p>
    <p>Comprimento: {{ tamanhoTermo() }}</p>
  `
})
export class BuscaSignalsComponent {
  buscaControl = new FormControl('');
  
  // Converte Observable de mudanças de valor para Signal
  termoBusca = toSignal(this.buscaControl.valueChanges, { initialValue: '' });

  // Computa valores derivados de forma reativa
  tamanhoTermo = computed(() => this.termoBusca()?.length || 0);
}

  1. Manipulação de Arrays Dinâmicos

Para formulários onde o usuário pode adicionar ou remover itens (como uma lista de telefones), utilizamos o FormArray.


import { Component } from '@angular/core';
import { ReactiveFormsModule, FormArray, FormControl, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-lista-dinamica',
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <div [formGroup]="perfilForm">
      <h3>Suas Habilidades</h3>
      <div formArrayName="habilidades">
        @for (item of listaHabilidades.controls; track $index) {
          <input [formControlName]="$index">
          <button (click)="remover($index)">X</button>
        }
      </div>
      <button (click)="adicionar()">Nova Habilidade</button>
    </div>
  `
})
export class ListaDinamicaComponent {
  perfilForm = new FormGroup({
    habilidades: new FormArray([new FormControl('')])
  });

  get listaHabilidades() {
    return this.perfilForm.get('habilidades') as FormArray;
  }

  adicionar() {
    this.listaHabilidades.push(new FormControl(''));
  }

  remover(index: number) {
    this.listaHabilidades.removeAt(index);
  }
}

  1. Validação Assíncrona

Em casos onde é necessário validar dados contra um servidor (ex: verificar se um e-mail já existe), o Angular fornece validadores assíncronos.


import { AbstractControl, ValidationErrors } from '@angular/forms';
import { delay, map, Observable, of } from 'rxjs';

export function validadorUnico(control: AbstractControl): Observable<ValidationErrors | null> {
  // Simulando chamada de API
  return of(control.value).pipe(
    delay(500),
    map(valor => valor === 'admin' ? { jaExiste: true } : null)
  );
}

Comparativo de Estratégias

Característica Template-driven Reativa (Reactive)
Configuração No HTML (Declarativa) No TypeScript (Imperativa)
Escalabilidade Baixa Alta
Validação Diretivas Funções
Testabilidade Complexa (depende do DOM) Simples (lógica isolada)
Cenários Dinâmicos Difícil implementação Suporte nativo (FormArray)

A escolha entre as duas abordagens depende da complexidade do seu projeto. Para fluxos de dados robustos e formulários que mudam dinamicamante, a abordagem reativa é superior. Para entradas rápidas e prototipagem, os formulários baseados em templates oferecem uma curva de aprendizado menor.

Tags: Angular ReactiveForms TemplateDrivenForms Signals TypeScript

Publicado em 6-30 05:30