Reactive Forms - Angular
Nos temas anteriores, foi abordado o método Template-Driven, que se concentra no template do componente. Para trabalhar com o formulário e seus elementos, eram aplicadas as diretivas NgModel
NgForm
Na abordagem Reactive Forms, é criado um conjunto de objetos FormGroup
FormControl
FormGroup
FormControl
myForm : FormGroup = new FormGroup();
Agora, vamos adicionar alguns elementos ao formulário:
myForm : FormGroup = new FormGroup({
"userName": new FormControl(),
"userEmail": new FormControl(),
"userPhone": new FormControl()
});
Aqui, foram definidos três elementos: userName
userEmail
userPhone
O objeto FormControl
myForm : FormGroup = new FormGroup({
"userName": new FormControl("Tom", Validators.required),
"userEmail": new FormControl("", [
Validators.required,
Validators.email
]),
"userPhone": new FormControl("", Validators.pattern("[0-9]{10}"))
});
Aqui, uma série de validadores foi aplicada aos elementos. O validador Validators.required
Validators.email
Validators.pattern("[0-9]{10}")
Para vincular o objeto myForm
formGroup
<form [formGroup]="myForm">
Além disso, é necessário conectar os objetos FormControl
formControlName
<input name="name" formControlName="userName" />
Esse elemento será associado ao objeto userName: new FormControl("Tom")
Agora, vamos ver como esses objetos interagem com o template do componente. Para isso, definimos o seguinte componente:
import { Component } from "@angular/core";
import { FormsModule, FormGroup, FormControl, Validators, ReactiveFormsModule } from "@angular/forms";
@Component({
selector: "my-app",
standalone: true,
imports: [FormsModule, ReactiveFormsModule],
styles: `
div {margin: 5px 0;}
.alert {color:red;}
input.ng-touched.ng-invalid {border:solid red 2px;}
input.ng-touched.ng-valid {border:solid green 2px;}
`,
template: `<form [formGroup]="myForm" novalidate (ngSubmit)="submit()">
<div>
<label>Nome</label><br>
<input name="name" formControlName="userName" />
@if(myForm.controls["userName"].invalid && myForm.controls["userName"].touched){
<div class="alert">Nome incorreto</div>
}
</div>
<div>
<label>Email</label><br>
<input name="email" formControlName="userEmail" />
@if(myForm.controls["userEmail"].invalid && myForm.controls["userEmail"].touched){
<div class="alert">Email incorreto</div>
}
</div>
<div>
<label>Telefone</label><br>
<input name="phone" formControlName="userPhone" />
@if(myForm.controls["userPhone"].invalid && myForm.controls["userPhone"].touched){
<div class="alert">Número de telefone incorreto</div>
}
</div>
<button [disabled]="myForm.invalid">Enviar</button>
</form>`
})
export class AppComponent {
myForm: FormGroup;
constructor() {
this.myForm = new FormGroup({
"userName": new FormControl("Tom", Validators.required),
"userEmail": new FormControl("", [
Validators.required,
Validators.email
]),
"userPhone": new FormControl("", Validators.pattern("[0-9]{11}"))
});
}
submit() {
console.log(this.myForm);
}
}
Para exibir erros de validação, utilizamos blocos div
*ngIf="myForm.controls['userName'].invalid && myForm.controls['userName'].touched"
Usando expressões como myForm.controls['userName']
Para que isso funcione corretamente, é necessário importar o módulo ReactiveFormsModule
import { ReactiveFormsModule } from "@angular/forms";

Definindo Validadores Personalizados
Além dos validadores internos, também podemos definir nossos próprios validadores. Por exemplo, vamos definir um validador personalizado dentro da classe do componente:
export class AppComponent {
myForm: FormGroup;
constructor() {
this.myForm = new FormGroup({
"userName": new FormControl("Tom", [Validators.required, this.userNameValidator]),
"userEmail": new FormControl("", [
Validators.required,
Validators.email
]),
"userPhone": new FormControl()
});
}
submit() {
console.log(this.myForm);
}
// Validador personalizado
userNameValidator(control: FormControl): { [s: string]: boolean } | null {
if (control.value === "admin") {
return { "userName": true };
}
return null;
}
}
Essencialmente, um validador personalizado é um método comum. Nesse caso, é o método userNameValidator
true
No exemplo acima, o validador verifica se o valor do campo é igual à string "admin". Se for, ele retorna o objeto { "userName": true }
null
Esse validador é então adicionado ao controle userName
"userName": new FormControl("Tom", [Validators.required, this.userNameValidator])
Se o valor inserido no campo de nome for "admin", o campo falhará na validação, e o formulário refletirá esse erro. A personalização de validadores é muito útil quando as validações internas não cobrem casos específicos.
Essa flexibilidade nos permite construir formulários que atendem às necessidades específicas de cada aplicação, garantindo que todas as validações sejam aplicadas de forma correta.

Arrays de Elementos e FormArray
Alguns elementos do formulário podem se referir à mesma característica. Por exemplo, um usuário pode ser solicitado a fornecer os números de telefone que ele possui. Pode haver vários números, mas todos representariam a mesma característica: "números de telefone". Sendo assim, faz sentido agrupar todos os campos de entrada de números de telefone em um array. No Angular, podemos implementar facilmente essa funcionalidade usando a classe FormArray
Vamos modificar o código do componente AppComponent
import { Component } from "@angular/core";
import { FormsModule, ReactiveFormsModule, FormGroup, FormControl, Validators, FormArray } from "@angular/forms";
@Component({
selector: "my-app",
standalone: true,
imports: [FormsModule, ReactiveFormsModule],
styles: `
div {margin: 5px 0;}
.alert {color:red;}
input.ng-touched.ng-invalid {border:solid red 2px;}
input.ng-touched.ng-valid {border:solid green 2px;}
`,
template: `<form [formGroup]="myForm" novalidate (ngSubmit)="submit()">
<div>
<label>Nome</label><br>
<input name="name" formControlName="userName" />
@if(myForm.controls["userName"].invalid && myForm.controls["userName"].touched){
<div class="alert">Nome incorreto</div>
}
</div>
<div>
<label>Email</label><br>
<input name="email" formControlName="userEmail" />
@if(myForm.controls["userEmail"].invalid && myForm.controls["userEmail"].touched){
<div class="alert">Email incorreto</div>
}
</div>
<div formArrayName="phones">
@for(phone of getFormsControls()["controls"]; track $index){
<div>
<label>Telefone</label><br>
<input formControlName="{{$index}}" />
</div>
}
</div>
<button (click)="addPhone()">Adicionar telefone</button>
<button [disabled]="myForm.invalid">Enviar</button>
</form>`
})
export class AppComponent {
myForm: FormGroup;
constructor() {
this.myForm = new FormGroup({
"userName": new FormControl("Tom", [Validators.required]),
"userEmail": new FormControl("", [
Validators.required,
Validators.email
]),
"phones": new FormArray([
new FormControl("+7", Validators.required)
])
});
}
getFormsControls(): FormArray {
return this.myForm.controls["phones"] as FormArray;
}
addPhone() {
(<FormArray>this.myForm.controls["phones"]).push(new FormControl("+7", Validators.required));
}
submit() {
console.log(this.myForm);
}
}
Agora, os campos de entrada para números de telefone são representados como um array:
"phones": new FormArray([
new FormControl("+7", Validators.required)
])
O FormArray
FormControl
Para permitir a adição dinâmica de novos controles de entrada, o método addPhone()
addPhone() {
(<FormArray>this.myForm.controls["phones"]).push(new FormControl("+7", Validators.required));
}
Nesta função, primeiro obtemos o array de controles usando this.myForm.controls["phones"]
FormArray
push
Para simplificar o acesso aos controles do array, foi criado o método getFormsControls()
FormArray
getFormsControls(): FormArray {
return this.myForm.controls["phones"] as FormArray;
}
No código HTML, os objetos do FormArray
@for
<div formArrayName="phones">
@for(phone of getFormsControls()["controls"]; track $index) {
<div>
<label>Telefone</label><br>
<input formControlName="{{$index}}" />
</div>
}
</div>
O container de todos os elementos de entrada é identificado pela diretiva formArrayName="phones"
formControlName="{{ $index }}"
Por fim, o botão "Adicionar telefone" permite adicionar um novo campo de entrada para inserir outro número de telefone ao formulário:
<button (click)="addPhone()">Adicionar telefone</button>
O exemplo acima demonstra como criar um formulário com um array de elementos de entrada. Essa abordagem é muito útil quando precisamos coletar várias entradas de um mesmo tipo.

FormBuilder
A classe FormBuilder
Aqui está um exemplo de como usar o FormBuilder:
import { Component } from "@angular/core";
import { FormsModule, ReactiveFormsModule, FormGroup,
FormControl, Validators, FormArray, FormBuilder } from "@angular/forms";
@Component({
selector: "my-app",
standalone: true,
imports: [FormsModule, ReactiveFormsModule],
styles: `
div {margin: 5px 0;}
.alert {color:red;}
input.ng-touched.ng-invalid {border:solid red 2px;}
input.ng-touched.ng-valid {border:solid green 2px;}
`,
template: `<form [formGroup]="myForm" novalidate (ngSubmit)="submit()">
<div>
<label>Nome</label><br>
<input name="name" formControlName="userName" />
@if(myForm.controls["userName"].invalid && myForm.controls["userName"].touched){
<div class="alert">Nome incorreto</div>
}
</div>
<div>
<label>Email</label><br>
<input name="email" formControlName="userEmail" />
@if(myForm.controls["userEmail"].invalid && myForm.controls["userEmail"].touched){
<div class="alert">Email incorreto</div>
}
</div>
<div formArrayName="phones">
@for(phone of getFormsControls()["controls"]; track $index){
<div>
<label>Telefone</label><br>
<input formControlName="{{$index}}" />
</div>
}
</div>
<button (click)="addPhone()">Adicionar telefone</button>
<button [disabled]="myForm.invalid">Enviar</button>
</form>`
})
export class AppComponent {
myForm: FormGroup;
constructor(private formBuilder: FormBuilder) {
this.myForm = formBuilder.group({
"userName": ["Tom", [Validators.required]],
"userEmail": ["", [Validators.required, Validators.email]],
"phones": formBuilder.array([
["+7", Validators.required]
])
});
}
getFormsControls(): FormArray {
return this.myForm.controls["phones"] as FormArray;
}
addPhone() {
(<FormArray>this.myForm.controls["phones"]).push(new FormControl("+7", Validators.required));
}
submit() {
console.log(this.myForm);
}
}
O FormBuilder
group()
array()
FormGroup
FormArray
O resultado final do componente é o mesmo que o exemplo anterior.