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.
- 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.
- 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);
}
- 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);
}
}
- 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.