Forms em Angular
Formulários são uma parte essencial no desenvolvimento web, e o Angular acertou em cheio na forma como se trabalha formulários. O Angular oferece duas abordagens principais para trabalhar com formulários: Template-driven Forms (Formulários Orientados por Template) e Reactive Forms (Formulários Reativos). Ambas possuem suas vantagens e são úteis em diferentes cenários.
Template-driven Forms:
Características:
- São mais simples de usar e mais adequados para formulários simples.
- A lógica é principalmente no template HTML.
- O estado e a validação dos inputs são gerenciados automaticamente pelo Angular.
O Template-driven Forms vem do módulo FormsModule então não pode esquecer de importa-lo no módulo que seu componente pertence, ou importar no componente se ele for standalone.
Exemplo Básico:
<form #myForm="ngForm" (ngSubmit)="onSubmit()">
<input type="text" name="username" ngModel required>
<input type="password" name="password" ngModel required minlength="6">
<button type="submit">Submit</button>
</form>
No componente:
@Component({
// ...
})
export class MyComponent {
onSubmit(form: NgForm) {
if (form.valid) {
console.log(form.value);
// Lógica para enviar os dados do formulário
}
}
}
Outra forma de usar o Template-driven Forms é através do conceito de Two-Way Binding (em resumo é um dado que pode ser alterado tanto pelo template quanto pela classe) através do [(ngModel)] o funcionamento é simples:
<input type="text" [(ngModel)]="firstName">
<p>O valor do campo é: {{ firstName }}</p>
export class MyComponent {
firstName: string = ''; // O valor inicial do campo
// Outras lógicas do componente...
}
Reactive Forms
Características
- Mais poderosos e escaláveis, ideais para formulários complexos.
- A lógica reside no componente TypeScript.
- Oferecem maior controle sobre a validação, estados e reatividade dos formulários.
O Reactive Forms será explicado usando o FormBuilder mas também existe a criação de formulários reativos usando o FormGroup diretamente, tem apenas algumas diferenças sutis, como não precisar injeta-lo como dependência.
O formulário reativo vem do módulo ReactiveFormsModule então não pode esquecer de importa-lo no módulo que seu componente pertence, ou importar no componente se ele for standalone.
FormBuilder
FormBuilder é uma forma de criar formulários mais complexos e mantê-lo organizado. Para entender como fazer um formulário com ele, vamos criar um formulário de inscrição de um evento.
Primeiramente, o FormBuilder é um serviço que precisa ser injetado no componente que o usará. Para criar um formulário, usamos o método FormBuilder.group que aguarda um objeto com o nome dos campos e cada campo será um array onde a primeira posição é o valor inicial e a segunda posição é opcional, que seria o lugar para inserir um validador ou um array de validadores. Com o exemplo será mais fácil de entender.
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
@Component({
selector: 'app-event-registration',
standalone: true,
imports: [ReactiveFormsModule],
templateUrl: './event-registration.component.html',
styleUrls: ['./event-registration.component.css']
})
export class EventRegistrationComponent {
registrationForm: FormGroup;
constructor(private formBuilder: FormBuilder) {
this.registrationForm = this.formBuilder.group({
firstName: ['', Validators.required],
lastName: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
phone: ['', Validators.required]
});
}
onSubmit() {
if (this.registrationForm.valid) {
console.log(this.registrationForm.value);
// Lógica para enviar os dados do formulário
}
}
}
Perceba que o tipo do formulário é FormGroup, que fornece vários métodos para trabalhar com o formulário, como é o exemplo do valid que verifica se todos os campos passaram nos validadores e o value que contém um objeto com o valor de todos os campos. Uma lista de algum dos métodos que o FormGroup possui:
| controls | é uma propriedade que retorna um objeto que contém todos os controles individuais (campos) do FormGroup. |
|---|---|
| setValue() | define os valores para todos os campos no FormGroup. Você deve passar um objeto com chaves correspondentes aos nomes dos campos e os valores que deseja atribuir a eles. |
| patchValue() | semelhante ao setValue(), mas permite definir valores apenas para os campos especificados, mantendo os valores atuais para os campos não especificados. |
| reset() | redefine o estado do formulário para os valores iniciais. |
| value | é uma propriedade que retorna um objeto contendo os valores atuais de todos os campos no FormGroup. |
| valid | é uma propriedade somente leitura que indica se o FormGroup é válido (ou seja, se todos os campos passaram nas validações). |
| errors | é uma propriedade que retorna um objeto contendo erros de validação atuais no FormGroup. Se o formulário estiver válido, será null. |
| dirty | é uma propriedade somente leitura que indica se o usuário modificou algum dos campos no FormGroup. |
| touched | é uma propriedade somente leitura que indica se o usuário clicou em algum dos campos no FormGroup. |
| get() | é usado para resgatar as propriedades de um elemento do formulário. Usado geralmente para verificar erros. |
*Para ver todos os métodos do FormGroup acesse a documentação oficial.
Agora para relacionar os campos do FormBuilder com o form e inputs no HTML , usamos a diretiva [formGroup] para relacionar o form ao registrationForm e nos inputs usamos o atributo formControlName para relacionar cada campo individual, veja no exemplo como fica.
<form [formGroup]="registrationForm" (ngSubmit)="onSubmit()">
<div>
<label for="firstName">Nome:</label>
<input type="text" id="firstName" formControlName="firstName">
</div>
<div>
<label for="lastName">Sobrenome:</label>
<input type="text" id="lastName" formControlName="lastName">
</div>
<div>
<label for="email">Email:</label>
<input type="email" id="email" formControlName="email">
</div>
<div>
<label for="phone">Telefone:</label>
<input type="tel" id="phone" formControlName="phone">
</div>
<button type="submit" [disabled]="!registrationForm.valid">Enviar</button>
</form>
Veja o que retorna do console.log(this.registrationForm.value) no onSubmit:

Validadores
Os validadores verificam são funções utilizadas para verificar se um campo de formulário atende a determinados critérios de validação. Existem validadores básicos já criados dentro do Angular mas principalmente é possível criar validadores personalizados.
Validadores Padrão:
- Validators.required: Verifica se o campo é obrigatório.
- Validators.minLength: Define um comprimento mínimo para uma string.
- Validators.maxLength: Define um comprimento máximo para uma string.
- Validators.pattern: Verifica se o valor corresponde a um padrão de expressão regular.
- Validators.email: Verifica se o valor é um endereço de e-mail válido.
Criando Validadores
Para criar um validador personalizado, basta criar uma função que retorne outra função do tipo ValidatorFn.
Vamos criar um exemplo de validador que verifica se a entrada é um número par:
import { AbstractControl, ValidatorFn, Validators } from '@angular/forms';
function evenNumberValidator(): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } | null => {
const value = control.value;
if (value % 2 !== 0) {
return { 'evenNumber': { value } };
}
return null;
};
}
A função evenNumberValidator retorna um ValidatorFn, que é uma função que recebe um AbstractControl, que seria um tipo de dado parecido com o FormGroup para aquele campo do formulário, e retorna um objeto com o nome do erro e a mensagem desse erro ou qualquer outra coisa se a validação falhar ou null se a validação for bem-sucedida.
Para usar esse validador em um campo do formulário, você pode passar essa função ao criar o controle, como no exemplo a seguir:
import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-my-component',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="myForm">
<input type="number" formControlName="numberField">
<div *ngIf="myForm.get('numberField').hasError('evenNumber')">
Por favor, insira um número par.
</div>
</form>
`
})
export class MyComponent {
myForm: FormGroup;
constructor(private formBuilder: FormBuilder) {
this.myForm = this.formBuilder.group({
numberField: ['', evenNumberValidator()]
});
}
}
Neste exemplo, evenNumberValidator() é utilizado para validar o campo numberField do formulário. Quando o valor inserido não é um número par, o erro evenNumber é ativado e pode ser verificado no template para exibir uma mensagem de erro ou qualquer outro feedback visual necessário.
Aqui está outro exemplo de um validator para verificar se a senha inserida é forte:
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
export function strongPasswordValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const value = control.value;
if (!value) return null;
const hasUppercase = /[A-Z]/.test(value);
const hasLowercase = /[a-z]/.test(value);
const hasNumber = /\d/.test(value);
const hasSymbol = /[$@#!&+-]/.test(value);
const hasInvalidChar = /[^a-zA-Z0-9$@#!&+-]/.test(value);
const errors: ValidationErrors = {};
if (!hasUppercase) errors['uppercase'] = true;
if (!hasLowercase) errors['lowercase'] = true;
if (!hasNumber) errors['number'] = true;
if (!hasSymbol) errors['symbol'] = true;
if (hasInvalidChar) errors['invalidChar'] = true;
//Verificando se tem alguma propriedade em errors
return Object.keys(errors).length ? errors : null;
};
}