Formulários Reativos
Imagine seus formulários como entidades vivas, com dados fluindo e se transformando em tempo real.
Os formulários reativos do Angular oferecem um controle preciso sobre esse fluxo, permitindo que você modele, manipule e valide dados de forma elegante e eficiente.
Este guia mostra como criar e atualizar um controle de formulário básico, progredir para o uso de vários controles em um grupo, validar valores do formulário e criar formulários dinâmicos onde você pode adicionar ou remover controles em tempo de execução.
Visão Geral dos Formulários Reativos: O Poder do Modelo Imutável
Ao contrário dos formulários tradicionais, onde os dados são mutáveis e as mudanças podem ser imprevisíveis, os formulários reativos adotam uma abordagem explícita e imutável. Cada interação do usuário gera um novo estado do formulário, preservando a integridade dos dados a cada passo.
No coração dos formulários reativos estão os observables, fluxos contínuos de valores que representam as entradas e o estado do formulário. Essa abordagem síncrona garante que você tenha acesso instantâneo e consistente aos dados, facilitando a manipulação e a validação em tempo real.
Comparando com Formulários Template-Driven: A Escolha da Ferramenta Certa
Enquanto os formulários template-driven oferecem uma abordagem mais simples e direta, baseada em diretivas e dados mutáveis, os formulários reativos se destacam pela sua robustez, escalabilidade e facilidade de teste. A escolha entre as duas abordagens depende da complexidade do seu projeto e das suas preferências de desenvolvimento.
Vantagens dos Formulários Reativos:
- Acesso síncrono ao modelo de dados: Manipule os dados do formulário de forma imediata e consistente, sem se preocupar com atrasos ou inconsistências.
- Imutabilidade com operadores de observables: Transforme e manipule os dados do formulário de forma segura e previsível, utilizando operadores poderosos como
map
,filter
edebounceTime
. - Rastreamento de alterações através de fluxos de observables: Monitore as mudanças no formulário em tempo real, facilitando a validação e a atualização da interface do usuário.
- Facilidade de teste: A natureza previsível dos formulários reativos simplifica a criação de testes unitários e de integração, garantindo a qualidade do seu código.
Desvendando os Segredos dos Formulários Reativos
Prepare-se para uma jornada emocionante pelo mundo dos formulários reativos em Angular. Nos próximos capítulos, exploraremos os blocos de construção essenciais, como FormControl
, FormGroup
e FormBuilder
, e aprenderemos a criar formulários dinâmicos e validados, capazes de lidar com as mais diversas necessidades da sua aplicação.
Adicionando um Controle de Formulário Básico: Os Primeiros Passos para a Maestria
Dominar os formulários reativos começa com a compreensão de como adicionar e manipular controles individuais. Cada controle, como um campo de entrada de texto, é representado por uma instância FormControl
, a unidade fundamental dos formulários reativos.
Gerando um Novo Componente e Criando um FormControl: Dando Vida ao Seu Controle
Utilize o Angular CLI para gerar um novo componente que irá abrigar o seu controle de formulário. Em seguida, dentro da classe do componente, instancie um novo FormControl
e defina seu valor inicial.
ng generate component components/name-editor --standalone
Em seguida, dentro da classe do componente, instancie um novo FormControl
e defina seu valor inicial.
import { Component } from '@angular/core';
import {FormControl, ReactiveFormsModule} from "@angular/forms";
@Component({
selector: 'app-name-editor',
standalone: true,
imports: [
ReactiveFormsModule
],
template: `
<label for="name">Name: </label>
<input id="name" type="text" [formControl]="name">
`,
styles: ``
})
export class NameEditorComponent {
name = new FormControl('');
}
Importando o Módulo ReactiveFormsModule: A Chave para o Reino Reativo
Observe que importamos o módulo ReactiveFormsModule
do pacote @angular/forms
.
Esse módulo contém as diretivas essenciais para o funcionamento dos formulários reativos, como formControl
, que conecta os elementos do formulário ao modelo de dados.
Registrando o Controle no Template: Conectando o Modelo à Visão
Conectamos o controle criado no componente ao elemento de formulário no template. Utilizamos a diretiva formControl
para estabelecer essa ligação.
Usando a sintaxe de vinculação no template, o controle de formulário agora está registrado no elemento de entrada name
no template. O controle de formulário e o elemento DOM se comunicam entre si: a visualização reflete as mudanças no modelo, e o modelo reflete as mudanças na visualização.
<label for="name">Nome: </label>
<input id="name" type="text" [formControl]="name">
Exibindo o Componente
O FormControl
atribuído à propriedade name
é exibido quando o componente <app-name-editor>
é adicionado a um template.
Exibindo o Componente
O FormControl
atribuído à propriedade name
é exibido quando o componente <app-name-editor>
é adicionado a um template.
src\app\app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import {NameEditorComponent} from "./components/app-name-editor/name-editor.component";
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, NameEditorComponent],
template: `
<h1>Welcome to {{title}}!</h1>
<app-name-editor></app-name-editor>
`,
styles: [],
})
export class AppComponent {
title = 'reactive-forms-module';
}
Exibindo um Valor de Controle de Formulário
Você pode exibir o valor das seguintes maneiras:
- Através do observável
valueChanges
, onde você pode ouvir mudanças no valor do formulário no template usandoAsyncPipe
ou na classe do componente usando o métodosubscribe()
. - Com a propriedade
value
, que fornece um instantâneo do valor atual.
O exemplo a seguir mostra como exibir o valor atual usando interpolação no template.
<p>Valor: {{ name.value }}</p>
O valor exibido muda à medida que você atualiza o elemento de controle do formulário.
Os formulários reativos fornecem acesso a informações sobre um determinado controle por meio de propriedades e métodos fornecidos com cada instância. Essas propriedades e métodos da classe subjacente AbstractControl
são usados para controlar o estado do formulário e determinar quando exibir mensagens ao lidar com validação de entrada.
Leia sobre outras propriedades e métodos do FormControl
na Referência da API.
Substituindo um Valor de Controle de Formulário
Os formulários reativos têm métodos para alterar o valor de um controle programaticamente, o que lhe dá flexibilidade para atualizar o valor sem a interação do usuário.
Uma instância de controle de formulário fornece um método setValue()
que atualiza o valor do controle de formulário e valida a estrutura do valor fornecido em relação à estrutura do controle.
Por exemplo, ao recuperar dados do formulário de uma API de backend ou serviço, use o método setValue()
para atualizar o controle para seu novo valor, substituindo completamente o valor antigo.
O exemplo a seguir adiciona um método à classe do componente para atualizar o valor do controle para Deadpool usando o método setValue()
.
updateName() {
this.name.setValue('Deadpool');
}
Atualize o template com um botão para simular uma atualização de nome.
Quando você clica no botão Update Name
, o valor inserido no elemento de controle do formulário é refletido como seu valor atual.
<label for="name">Nome: </label>
<input id="name" type="text" [formControl]="name">
<p>Valor: {{ name.value }}</p>
<button type="button" (click)="updateName()">Update Name</button>
O modelo de formulário é a fonte da verdade para o controle, então quando você clica no botão, o valor da entrada é alterado dentro da classe do componente, substituindo seu valor atual.
Dica Útil
Neste exemplo, você está usando um controle único. Ao usar o método setValue()
com uma instância de grupo de formulário (form group
) ou array de formulário (form array
), o valor precisa corresponder à estrutura do grupo ou array.
Essa seção mostrou como adicionar e gerenciar um controle de formulário básico usando formulários reativos no Angular.
Nos próximos tópicos, exploraremos como agrupar controles de formulário, validar entradas e criar formulários dinâmicos para aplicações mais complexas.
Agrupando Controles de Formulário (Form Groups)
Em formulários, é comum ter controles relacionados. Formulários reativos no Angular oferecem duas formas de agrupar controles:
- Form Groups: Define um formulário com um conjunto fixo de controles gerenciados juntos. É o que veremos aqui.
- Form Arrays: Define um formulário dinâmico, onde você pode adicionar e remover controles em tempo de execução.
Assim como uma instância de controle de formulário oferece controle sobre um único campo de entrada, uma instância de grupo de formulário rastreia o estado do formulário de um grupo de instâncias de controle de formulário (por exemplo, um formulário).
Cada controle em uma instância de grupo de formulário é rastreado por nome ao criar o grupo de formulário. O exemplo a seguir mostra como gerenciar múltiplas instâncias de controle de formulário em um único grupo.
Gerar o Componente ProfileEditor
Para começar, vamos gerar um novo componente chamado ProfileEditor
e importar as classes FormGroup
e FormControl
do pacote @angular/forms
.
Comando:
ng generate component components/ProfileEditor --standalone
import { Component } from '@angular/core';
import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms";
@Component({
selector: 'app-profile-editor',
standalone: true,
imports: [
ReactiveFormsModule
],
template: `
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<label for="first-name">First Name: </label>
<input id="first-name" type="text" formControlName="firstName">
<label for="last-name">Last Name: </label>
<input id="last-name" type="text" formControlName="lastName">
<p>Complete o formulário para habilitar o botão.</p>
<button type="submit" [disabled]="!profileForm.valid">Submit</button>
</form>2,80
`,
styles: ``
})
export class ProfileEditorComponent {
profileForm = new FormGroup({
firstName: new FormControl(''),
lastName: new FormControl('')
});
onSubmit() {
// TODO: Use EventEmitter with form value
console.warn(this.profileForm.value);
}
}
Exibir o Componente
Para exibir o componente ProfileEditor
que contém o formulário, adicione-o ao template principal da aplicação.
Código:
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import {NameEditorComponent} from "./components/app-name-editor/name-editor.component";
import {ProfileEditorComponent} from "./components/profile-editor/profile-editor.component";
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, NameEditorComponent, ProfileEditorComponent],
template: `
<h1>Welcome to {{title}}!</h1>
<app-profile-editor></app-profile-editor>
`,
styles: [],
})
export class AppComponent {
title = 'reactive-forms-module';
}
Criando Grupos de Formulários Aninhados
Os grupos de formulários podem aceitar tanto instâncias de controle de formulário individuais quanto outras instâncias de grupo de formulário como filhos. Isso torna a composição de modelos de formulário complexos mais fácil de manter e agrupar logicamente.
Para criar um grupo aninhado em profileForm
, adicione um elemento address
aninhado à instância do grupo de formulário.
Código:
profileForm = new FormGroup({
firstName: new FormControl(''),
lastName: new FormControl(''),
address: new FormGroup({
street: new FormControl(''),
city: new FormControl(''),
state: new FormControl(''),
zip: new FormControl('')
})
});
Template do Grupo de Formulário Aninhado
Atualize o template para conectar a instância do grupo de formulário e seus elementos de entrada. Adicione o grupo de formulário address
contendo os campos street
, city
, state
e zip
ao template do ProfileEditor
.
<!-- src/app/profile-editor/profile-editor.component.html -->
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<label for="first-name">First Name: </label>
<input id="first-name" type="text" formControlName="firstName">
<label for="last-name">Last Name: </label>
<input id="last-name" type="text" formControlName="lastName">
<div formGroupName="address">
<h2>Address</h2>
<label for="street">Street: </label>
<input id="street" type="text" formControlName="street">
<label for="city">City: </label>
<input id="city" type="text" formControlName="city">
<label for="state">State: </label>
<input id="state" type="text" formControlName="state">
<label for="zip">Zip Code: </label>
<input id="zip" type="text" formControlName="zip">
</div>
<p>Complete o formulário para habilitar o botão.</p>
<button type="submit" [disabled]="!profileForm.valid">Submit</button>
</form>
O formulário ProfileEditor
é exibido como um grupo, mas o modelo é subdividido para representar áreas de agrupamento lógico.
Atualizando Partes do Modelo de Dados
Ao lidar com formulários complexos com múltiplos controles, muitas vezes você precisará atualizar apenas partes específicas do modelo de dados.
O Angular oferece duas maneiras de fazer isso:
Método | Detalhes |
---|---|
setValue() | Substitui o valor inteiro do controle ou grupo, requerendo um objeto completo. |
patchValue() | Atualiza apenas as propriedades especificadas no objeto, ignorando as demais. |
setValue()
:
- Uso: Define um novo valor para um controle individual ou para o grupo inteiro.
- Estrito: Requer que você forneça um objeto que corresponda exatamente à estrutura do seu
FormGroup
. Se faltar alguma propriedade ou a estrutura estiver incorreta, um erro será lançado. - Exemplo:
this.profileForm.setValue({
firstName: 'João',
lastName: 'Silva',
address: {
street: 'Rua Principal, 123',
city: 'São Paulo',
state: 'SP',
zip: '01001-000'
}
});
patchValue()
:
- Uso: Atualiza apenas as propriedades especificadas no objeto que você fornece, ignorando as outras.
- Flexível: Não exige que você forneça um objeto completo, apenas as propriedades que deseja modificar.
- Exemplo:
this.profileForm.patchValue({
firstName: 'Maria',
address: {
street: 'Avenida Secundária, 456'
}
});
Quando Usar Cada Método
setValue()
: Ideal quando você precisa redefinir todo o formulário ou um grupo aninhado para um novo estado inicial.patchValue()
: Útil para atualizar partes específicas do formulário, como quando o usuário edita apenas alguns campos.
No ProfileEditorComponent
, use o método updateProfile
com o seguinte exemplo para atualizar o primeiro nome e o endereço do usuário.
updateProfile() {
this.profileForm.patchValue({
firstName: 'Nancy',
address: {
street: '123 Drew Street'
}
});
}
Simule uma atualização adicionando um botão ao template para atualizar o perfil do usuário sob demanda.
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<label for="first-name">First Name: </label>
<input id="first-name" type="text" formControlName="firstName">
<label for="last-name">Last Name: </label>
<input id="last-name" type="text" formControlName="lastName">
<div formGroupName="address">
<h2>Address</h2>
<label for="street">Street: </label>
<input id="street" type="text" formControlName="street">
<label for="city">City: </label>
<input id="city" type="text" formControlName="city">
<label for="state">State: </label>
<input id="state" type="text" formControlName="state">
<label for="zip">Zip Code: </label>
<input id="zip" type="text" formControlName="zip">
</div>
<button type="button" (click)="updateProfile()">Update Profile</button>
<p>Complete o formulário para habilitar o botão.</p>
<button type="submit" [disabled]="!profileForm.valid">Submit</button>
</form>
Quando um usuário clica no botão, o modelo profileForm
é atualizado com novos valores para firstName
e street
. Observe que street
é fornecido em um objeto dentro da propriedade address
.
Isso é necessário porque o método patchValue()
aplica a atualização contra a estrutura do modelo. PatchValue()
apenas atualiza as propriedades que o modelo do formulário define.
Usando o Serviço FormBuilder para Gerar Controles
Criar instâncias de controle de formulário manualmente pode se tornar repetitivo ao lidar com vários formulários. O serviço FormBuilder
fornece métodos convenientes para gerar controles.
Use os seguintes passos para aproveitar este serviço.
- Importe a classe
FormBuilder
. - Injeção do serviço
FormBuilder
. - Gere o conteúdo do formulário.
Os exemplos a seguir mostram como refatorar o componente ProfileEditor
para usar o serviço FormBuilder
para criar instâncias de controle de formulário e grupo de formulário.
Importar a Classe FormBuilder
Importe a classe FormBuilder
do pacote @angular/forms
.
Código:
// src/app/profile-editor/profile-editor.component.ts
import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';
Injetar o Serviço FormBuilder
O serviço FormBuilder
é um provedor injetável que é fornecido com o módulo de formulários reativos. Injete essa dependência adicionando-a ao construtor do componente.
export class ProfileEditorComponent {
constructor(private formBuilder: FormBuilder) {}
// ...
}
Gerar Controles de Formulário
O serviço FormBuilder
tem três métodos: control()
, group()
e array()
. Estes são métodos de fábrica para gerar instâncias em suas classes de componente, incluindo controles de formulário, grupos de formulário e arrays de formulário. Use o método group
para criar os controles profileForm
.
Código:
gora você pode usar os métodos control()
, group()
e array()
do FormBuilder
para criar seus controles:
profileForm = this.formBuilder.group({
firstName: [''],
lastName: [''],
address: this.formBuilder.group({
street: [''],
city: [''],
state: [''],
zip: [''],
})
});
Explicação do Código
this.formBuilder.group({})
: Cria um novoFormGroup
. O objeto passado como argumento define a estrutura do grupo, com cada chave representando o nome de um controle e o valor sendo um array com:- O valor inicial do controle (neste caso, strings vazias).
- Opcionalmente, validadores síncronos (segundo elemento do array).
- Opcionalmente, validadores assíncronos (terceiro elemento do array).
this.formBuilder.control('')
: Cria um novoFormControl
com o valor inicial especificado.
Comparação com a Criação Manual
Criação Manual | Criação com FormBuilder |
---|---|
new FormGroup({ firstName: new FormControl(”) }) | this.formBuilder.group({ firstName: [”] }) |
new FormGroup({ address: new FormGroup({ street: new FormControl(”) }) }) | this.formBuilder.group({ address: this.formBuilder.group({ street: [”] }) }) |
FormBuilder
com a criação das instâncias manualmente.import { Component } from '@angular/core';
import {FormBuilder, FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms";
@Component({
selector: 'app-profile-editor',
standalone: true,
imports: [
ReactiveFormsModule
],
template: `
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<label for="first-name">First Name: </label>
<input id="first-name" type="text" formControlName="firstName">
<label for="last-name">Last Name: </label>
<input id="last-name" type="text" formControlName="lastName">
<div formGroupName="address">
<h2>Address</h2>
<label for="street">Street: </label>
<input id="street" type="text" formControlName="street">
<label for="city">City: </label>
<input id="city" type="text" formControlName="city">
<label for="state">State: </label>
<input id="state" type="text" formControlName="state">
<label for="zip">Zip Code: </label>
<input id="zip" type="text" formControlName="zip">
</div>
<button type="button" (click)="updateProfile()">Update Profile</button>
<p>Complete o formulário para habilitar o botão.</p>
<button type="submit" [disabled]="!profileForm.valid">Submit</button>
</form>
`,
styles: ``
})
export class ProfileEditorComponent {
constructor(private formBuilder: FormBuilder) {}
profileForm = this.formBuilder.group({
firstName: [''],
lastName: [''],
address: this.formBuilder.group({
street: [''],
city: [''],
state: [''],
zip: ['']
})
});
onSubmit() {
// TODO: Use EventEmitter with form value
console.warn(this.profileForm.value);
}
updateProfile() {
this.profileForm.patchValue({
firstName: 'Nancy',
address: {
street: '123 Drew Street'
}
});
}
}
Validando Entradas do Formulário
A validação de formulário é fundamental para garantir que os dados inseridos pelo usuário estejam completos e corretos. Vamos começar adicionando um validador simples: o campo obrigatório.
Importar uma Função de Validador
Os formulários reativos incluem um conjunto de funções de validador para casos de uso comuns. Essas funções recebem um controle para validar e retornam um objeto de erro ou um valor nulo com base na verificação de validação.
Importe a classe Validators
do pacote @angular/forms
.
// reactive-forms-module/src/app/components/profile-editor/profile-editor.component.ts
import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
Tornar um Campo Obrigatório
No componente ProfileEditor
, adicione o método estático Validators.required
como o segundo item no array para o controle firstName
.
profileForm = this.formBuilder.group({
firstName: ['', Validators.required], // Campo obrigatório
// ... outros campos ...
});
Exibir o Status do Formulário
Quando você adiciona um campo obrigatório ao controle de formulário, seu status inicial é inválido. Esse status inválido é propagado para o elemento do grupo de formulário pai, tornando seu status inválido. Acesse o status atual da instância do grupo de formulário através de sua propriedade status
.
Exiba o status atual do profileForm
usando interpolação.
Código:
<p>Form Status: {{ profileForm.status }}</p>
O botão Submit
está desativado porque profileForm
é inválido devido ao controle firstName
obrigatório. Depois de preencher o campo firstName
, o formulário se torna válido e o botão Submit
é habilitado.
<button type="submit" [disabled]="!profileForm.valid">Submit</button>
O botão Submit
está desativado porque profileForm
é inválido devido ao controle firstName
obrigatório. Depois de preencher o campo firstName
, o formulário se torna válido e o botão Submit
é habilitado.
Explicações Detalhadas
Validators
: Classe que contém funções de validação prontas para uso, comorequired
,email
,minLength
, etc.Validators.required
: Função validadora que verifica se o campo não está vazio.profileForm.status
: Propriedade doFormGroup
que retorna o status atual do formulário (VALID
,INVALID
,PENDING
,DISABLED
).profileForm.valid
: Propriedade doFormGroup
que retornatrue
se todos os controles do formulário forem válidos efalse
caso contrário.
Criando Formulários Dinâmicos com FormArray
FormArray
é uma alternativa ao FormGroup
para gerenciar qualquer número de controles não nomeados. Assim como instâncias de grupos de formulário, você pode inserir e remover controles dinamicamente de instâncias de arrays de formulário, e o valor e o status de validação da instância do array de formulário são calculados a partir de seus controles filhos.
No entanto, você não precisa definir uma chave para cada controle pelo nome, então essa é uma ótima opção se você não souber o número de valores filhos com antecedência.
Para definir um formulário dinâmico, siga os seguintes passos:
- Importe a classe
FormArray
. - Defina um controle
FormArray
. - Acesse o controle
FormArray
com um método getter. - Exiba o array de formulário em um template.
O exemplo a seguir mostra como gerenciar um array de aliases
no ProfileEditor
.
Importar a Classe FormArray
Importe a classe FormArray
do pacote @angular/forms
para usar nas informações de tipo. O serviço FormBuilder
está pronto para criar uma instância de FormArray
.
Código:
import { FormBuilder, Validators, FormArray } from '@angular/forms';
Definir um Controle FormArray
Você pode inicializar um array de formulário com qualquer número de controles, de zero a muitos, definindo-os em um array. Adicione uma propriedade aliases
à instância do grupo de formulário profileForm
para definir o array de formulário.
Use o método FormBuilder.array()
para definir o array e o método FormBuilder.control()
para preencher o array com um controle inicial.
Código:
profileForm = this.formBuilder.group({
// ... outros campos ...
aliases: this.formBuilder.array([
this.formBuilder.control('') // Controle inicial para o primeiro alias
])
});
Criar um Getter para o FormArray
Acessar o Controle FormArray
Um getter fornece acesso aos aliases na instância do array de formulário em comparação com a repetição do método profileForm.get()
para obter cada instância.
A instância do array de formulário representa um número indefinido de controles em um array. É conveniente acessar um controle através de um getter, e essa abordagem é fácil de repetir para controles adicionais.
Use a sintaxe de getter para criar uma propriedade de classe aliases
para recuperar o controle do array de formulário dos aliases a partir do grupo de formulário pai.
get aliases() {
return this.profileForm.get('aliases') as FormArray;
}
Adicionar um Método para Inserir um Novo Alias
Como o controle retornado é do tipo AbstractControl
, você precisa fornecer um tipo explícito para acessar a sintaxe do método para a instância do array de formulário. Defina um método para inserir dinamicamente um controle de alias no array de aliases. O método FormArray.push()
insere o controle como um novo item no array.
addAlias() {
this.aliases.push(this.formBuilder.control(''));
}
O controle aliases
na instância do grupo de formulário agora está populado com um controle único até que mais controles sejam adicionados dinamicamente.
Exibir o Array de Formulário no Template
Para anexar os aliases do seu modelo de formulário, você deve adicioná-lo ao template. Semelhante ao formGroupName
fornecido pela FormGroupNameDirective
, formArrayName
vincula a comunicação da instância do array de formulário ao template com a FormArrayNameDirective
.
Adicione o seguinte HTML ao template após o fechamento da tag div
do elemento formGroupName
.
Código:
<div formArrayName="aliases">
<h2>Aliases</h2>
<button type="button" (click)="addAlias()">Add Alias</button>
<div *ngFor="let alias of aliases.controls; let i=index">
<label for="alias-{{ i }}">Alias:</label>
<input id="alias-{{ i }}" type="text" [formControlName]="i">
</div>
</div>
Explicações Detalhadas
A diretiva *ngFor
itera sobre cada instância de controle de formulário fornecida pela instância do array de formulário aliases
. Como os elementos do array de formulário não são nomeados, você atribui o índice à variável i
e passa para cada controle para vinculá-lo à entrada formControlName
.
Cada vez que uma nova instância de alias é adicionada, a nova instância do array de formulário é fornecida com seu controle baseado no índice. Isso permite rastrear cada controle individual ao calcular o status e o valor do controle raiz.
FormArray
: Uma classe que representa um array de controles de formulário.formBuilder.array()
: Método doFormBuilder
para criar um novoFormArray
.formArrayName
: Diretiva que vincula um elemento HTML a umFormArray
no componente.aliases.controls
: Propriedade doFormArray
que retorna um array com os controles filhos.[formControlName]="i"
: Vincula cada campo de entrada ao controle correspondente noFormArray
, usando o índicei
do loop*ngFor
.
Adicionar um Alias
Inicialmente, o formulário contém um campo Alias
. Para adicionar outro campo, clique no botão Add Alias
. Você também pode validar o array de aliases relatado pelo modelo de formulário exibido pelo Form Value
na parte inferior do template.
Em vez de uma instância de controle de formulário para cada alias, você pode compor outra instância de grupo de formulário com campos adicionais. O processo de definir um controle para cada item é o mesmo.
Exemplo Completo
import { Component } from '@angular/core';
import {FormArray, FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms";
import {NgForOf} from "@angular/common";
@Component({
selector: 'app-profile-editor',
standalone: true,
imports: [
ReactiveFormsModule,
NgForOf
],
template: `
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<label for="first-name">First Name: </label>
<input id="first-name" type="text" formControlName="firstName">
<label for="last-name">Last Name: </label>
<input id="last-name" type="text" formControlName="lastName">
<div formGroupName="address">
<h2>Address</h2>
<label for="street">Street: </label>
<input id="street" type="text" formControlName="street">
<label for="city">City: </label>
<input id="city" type="text" formControlName="city">
<label for="state">State: </label>
<input id="state" type="text" formControlName="state">
<label for="zip">Zip Code: </label>
<input id="zip" type="text" formControlName="zip">
</div>
<div formArrayName="aliases">
<h2>Aliases</h2>
<button type="button" (click)="addAlias()">Add Alias</button>
<div *ngFor="let alias of aliases.controls; let i=index">
<label for="alias-{{ i }}">Alias:</label>
<input id="alias-{{ i }}" type="text" [formControlName]="i">
</div>
</div>
<button type="button" (click)="updateProfile()">Update Profile</button>
<p>Form Status: {{ profileForm.status }}</p>
<p>Complete o formulário para habilitar o botão.</p>
<button type="submit" [disabled]="!profileForm.valid">Submit</button>
</form>
`,
styles: ``
})
export class ProfileEditorComponent {
constructor(private formBuilder: FormBuilder) {}
profileForm = this.formBuilder.group({
firstName: ['',Validators.required],
lastName: [''],
address: this.formBuilder.group({
street: [''],
city: [''],
state: [''],
zip: ['']
}),
aliases: this.formBuilder.array([this.formBuilder.control('')])
});
onSubmit() {
// TODO: Use EventEmitter with form value
console.warn(this.profileForm.value);
}
updateProfile() {
this.profileForm.patchValue({
firstName: 'Nancy',
address: {
street: '123 Drew Street'
}
});
}
get aliases() {
return this.profileForm.get('aliases') as FormArray;
}
addAlias() {
this.aliases.push(this.formBuilder.control(''));
}
}
Resumo da API de Formulários Reativos do Angular
A tabela a seguir resume as classes e diretivas principais usadas para criar e gerenciar formulários reativos no Angular. Para detalhes completos da sintaxe, consulte a documentação de referência da API para o pacote @angular/forms
.
Classes Base e Serviços
Classe | Descrição |
---|---|
AbstractControl | Classe base abstrata para as classes concretas FormControl , FormGroup e FormArray . Fornece comportamentos e propriedades comuns. |
FormControl | Gerencia o valor e o status de validade de um controle de formulário individual (input, select, textarea). |
FormGroup | Gerencia o valor e o status de validade de um grupo de instâncias de AbstractControl . |
FormArray | Gerencia o valor e o status de validade de um array de instâncias de AbstractControl . |
FormBuilder | Serviço injetável que fornece métodos para criar instâncias de controles de forma mais concisa. |
FormRecord | Gerencia o valor e o status de validade de uma coleção de instâncias de FormControl com o mesmo tipo de valor. |
Diretivas
Diretiva | Detalhes |
---|---|
FormControlDirective | Sincroniza uma instância de FormControl autônoma com um elemento de controle de formulário. |
FormControlName | Sincroniza FormControl em uma instância FormGroup existente com um elemento de controle de formulário pelo nome. |
FormGroupDirective | Sincroniza uma instância FormGroup existente com um elemento DOM. |
FormGroupName | Sincroniza uma instância FormGroup aninhada com um elemento DOM. |
FormArrayName | Sincroniza uma instância FormArray aninhada com um elemento DOM. |
Neste guia, exploramos como criar e gerenciar formulários reativos no Angular, incluindo a adição de controles básicos, a criação de grupos de controles, a validação de entradas e a construção de formulários dinâmicos. Utilizamos o serviço FormBuilder
para simplificar a criação de controles e discutimos a importância de usar FormGroup
e FormArray
para organizar e gerenciar grupos de controles de formulário.
Os formulários reativos fornecem uma abordagem poderosa e flexível para gerenciar a entrada do usuário em suas aplicações Angular.
A abordagem baseada em modelos e a integração com observáveis tornam a sincronização de dados e o rastreamento de mudanças uma tarefa direta e eficiente. Com a capacidade de adicionar validações, agrupar controles e criar formulários dinâmicos, os formulários reativos são uma escolha excelente para aplicações de qualquer tamanho e complexidade.
Que esta jornada pelo mundo dos formulários reativos no Angular seja apenas o começo da sua odisseia pela maestria. Que cada formulário que você criar seja um monumento à sua dedicação, um testemunho do seu talento e uma fonte de inspiração para outros exploradores do universo digital.
Desvende os Segredos do Código:
Para aprofundar sua jornada e explorar os exemplos completos deste guia, convido você a visitar o repositório no GitHub: