Como aplicar validação em formulários reativos no Angular?
Você já deve ter preenchido um formulário que não indicava claramente o porquê de algum campo não estar válido e teve que ficar adivinhando quantos caracteres sua senha precisava ter ou que tipo de caracteres deveria receber. Ou, em um formulário longo, já ocorreu de preencher apenas os campos que achava serem requeridos e só no final, ao tentar submeter os dados, se deparou com inúmeras mensagens de erro mostrando que diversos outros campos eram obrigatórios? Chato, né? Vem aprender como melhorar essa experiência com o uso das validações customizadas do Angular!
Neste artigo, você vai aprender como:
- Criar um formulário reativo;
- Aplicar validações padrão e criar validações customizadas;
- Mostrar os erros de validação apenas quando o campo for acessado;
- Habilitar o botão de submeter os dados apenas quando o formulário estiver válido.
Vamos lá?
Criar formulários é algo muito comum na rotina de devs front-end. Tanto em formulários mais simples e, principalmente à medida que a quantidade de campos do formulário cresce, é necessário implementar validações.
As validações são importantes para prevenir erros de cadastro e garantir que as informações preenchidas estejam no formato esperado, mas é preciso deixar claro todas as particularidades dos campos a serem preenchidos, a fim de promover uma interação mais dinâmica e amigável das pessoas com nossa aplicação.
O que são formulários reativos?
No Angular, existem dois tipos diferentes de formulários: template drive e data driven. Os do tipo template driven são criados e configurados no component.html
; as validações também são incluídas no template e os valores do formulário são submetidos através da diretiva ngSubmit.
Já os formulários data driven (orientados a dados) são criados e configurados no component.ts
e a maior parte do código fica nesse arquivo e não no html. Dessa forma, temos um template mais limpo, apenas com a estrutura básica do formulário, já que todas as validações são feitas no componente. No html é feita a associação do template ao componente e não precisamos obrigatoriamente do ngSubmit.
Fáceis de criar e de dar manutenção, os formulários reativos, como também são conhecidos, são bastante utilizados devido ao seu poder e capacidade de conseguir reagir a mudanças que acontecem no formulário, utilizando observables.
Agora nós vamos entender como podemos criar um formulário reativo e aplicar validações nele.
O que precisamos configurar?
Com a aplicação criada, precisamos importar o reactiveformsModule
no arquivo app.module.ts
e colocar no array de imports do ngModule
, assim:
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
ReactiveFormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Se você tem dúvidas nesse passo, veja aqui como começar com o Angular e como criar sua primeira aplicação.
Agora, vamos criar no “app.component.ts” uma variável chamada formulario do tipo FormGroup (deve ser importado também), que é uma classe do Angular que vai nos ajudar na implementação e validação dos forms. Outra classe importante que veremos mais à frente é o FormControl.
import { FormGroup } from '@angular/forms';
formulario: FormGroup;
A abordagem que vamos utilizar para criar nosso formulário é através da injeção de dependência do service FormBuilder. Esse serviço fornece métodos para gerar controles de formulários e evita a criação manual de instâncias de controle. Para isso vamos ter que:
- Importar a classe FormBuilder;
- Injetar o service FormBuilder;
- Gerar o conteúdo do formulário.
Vamos criar um formulário com 4 campos:
- nome
- username
- senha
Nosso app.component.ts
ficará assim:
import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
formulario: FormGroup;
constructor(private formBuilder: FormBuilder) { }
ngOnInit(): void {
this.formulario = this.formBuilder.group({
nome: [''],
username: [''],
email: [''],
senha: ['']
});
}
}
Também precisamos criar a estrutura do formulário no template com os quatro campos. Agora, antes de implementar as validações, vamos fazer a sincronização do formulário criado no componente com o template para vermos se está tudo certo.
Para isso podemos usar uma diretiva do ReactiveFormsModule
chamada formGroup. E na tag form do html vamos atribuir a diretiva à nossa variável formulario e fazer um property binding, porque sempre que o campo for modificado, queremos atualizar e atribuir o valor ao formulário.
<form [formGroup]="formulario"></form>
Para associar cada input utilizamos outra diretiva chamada formControlName, fazendo um link entre o campo no html e o componente e passando exatamente o nome das variáveis que criamos através do FormBuilder.
<input id="nome" type="text" formControlName="nome" >
<input id="username" type="text" formControlName="username" >
<input id="email" type="email" formControlName="email" >
<input id="senha" type="password" formControlName="senha" >
Nosso formulário está assim (estilizado com o Angular Material).
Aplicando as validações
Agora, vamos incluir as seguintes validações na aplicação:
- Todos os campos serão de preenchimento obrigatório;
- O campo username aceitará apenas letras minúsculas;
- Verificação do preenchimento correto do formato de e-mail;
- Quantidade mínima de caracteres para a senha será 6.
Para começar, vamos importar a classe Validators:
import { Validators } from '@angular/forms';
Essa classe já traz vários métodos de validação prontos e fáceis de usar como por exemplo:
- required() - campo de preenchimento obrigatório;
- maxLength() - quantidade máxima de caracteres permitido;
- minLength() - quantidade mínima de caracteres permitido;
- email() - valida o formato de e-mail;
Veja aqui a lista completa de métodos da classe Validators.
Agora vamos implementar as validações no formulário, passando os métodos de validação como segundo parâmetro do array criado no component.ts
.
ngOnInit(): void {
this.formulario = this.formBuilder.group({
nome: ['', [Validators.required]],
username: ['', [Validators.required]],
email: ['', [Validators.required, Validators.email]],
senha: ['', [Validators.required, Validators.minLength(6)]]
});
}
Exibindo mensagens de erro
Para exibir mensagens referentes às validações, vamos incluir no html, abaixo do input, uma div com a diretiva *ngIf, passando o formulário e pegando os erros do campo passado como parâmetro, e incluindo a mensagem que queremos apresentar:
<div *ngIf="formulario.get('nome')?.errors>
Nome obrigatório
</div>
Vamos replicar isso em cada campo e ver o resultado:
Ok, agora as mensagens estão aparecendo, mas já no carregamento inicial da aplicação. Será que existe uma forma mais amigável de apresentá-las? Apenas quando o campo for acessado? Sim!! Podemos fazer isso através de uma propriedade do FormControl chamada touched. Seu valor inicial é false e sempre que o input dispara o evento onBlur, ou seja, quando o campo é acessado e perde o foco, a propriedade recebe o valor true.
No exemplo do campo nome ficará assim:
<div *ngIf="formulario.get('nome')?.errors?.['required'] && formulario.get('nome')?.touched>
Nome obrigatório
</div>
Replicando para os outros campos, agora as mensagens de erro aparecem apenas quando necessário.
Criando validações customizadas
A validação que queremos incluir para que o campo username receba apenas letras minúsculas não está presente na classe Validators
. Mas não tem problema, pois é possível criar inúmeras validações customizadas que atendam às suas necessidades.
No nosso exemplo, criamos um arquivo chamado minusculoValidator.ts
, importamos a classe Abstract Control e criamos a lógica da validação.
minusculoValidator.ts
:
import { AbstractControl } from "@angular/forms";
export function minusculoValidator(control: AbstractControl) {
const username = control.value as string;
if(username !== username?.toLowerCase()) {
return { minusculo: true };
} else
return null;
}
Depois disso, importamos a validação no componente e incluímos no array junto com as outras validações.
app.component.ts
:
username: ['', [Validators.required, minusculoValidator]],
app.component.html
:
<div *ngIf="formulario.get('username')?.errors?.['minusculo’] && formulario.get('username')?.touched">
Esse campo aceita apenas letras minúsculas"
</div>
Assim, através da propriedade errors, podemos criar mensagens customizadas dependendo do erro que seja disparado.
Desabilitar/Habilitar o botão
Outro ponto importante é que o botão para envio dos dados está habilitado desde o início, mesmo que os campos não tenham sido preenchidos. Vamos resolver isso?
O formulário possui a propriedade valid e podemos fazer um property binding atribuindo-a à propriedade disabled do botão e assim criar uma lógica para que o botão fique habilitado apenas quando o formulário estiver válido, ou seja, quando todos os campos forem preenchidos corretamente.
app.component.html
:
<button [disabled]="!formulario.valid">
Cadastre-se
</button>
Aqui temos o resultado final:
Conclusão
Neste artigo, nós aprendemos como criar um formulário reativo utilizando o serviço FormBuilder, como aplicar validações através da classe Validators e criar validações customizadas. Além disso, vimos como mostrar mensagens de erro personalizadas e habilitar/desabilitar o botão de envio de dados de acordo com o status do formulário.
Quer aprender mais sobre Angular? Veja nossa formação e aplique tudo o que aprendeu sobre validação de formulários reativos.