Artigo original: https://www.freecodecamp.org/news/how-to-validate-angular-template-driven-forms/
Introdução
Neste artigo, aprenderemos sobre validações em formulários do Angular orientados a templates. Criaremos um formulário simples de registro de usuário e implementaremos algumas validações embutidas nele. Junto às validações embutidas, implementaremos algumas validações customizadas para o formulário orientado a templates.
Consideraremos as seguintes validações customizadas para essa demonstração:
- Verificação de disponibilidade do nome de usuário;
- Validação de padrão de senha;
- Correspondência da senha inserida em dois campos diferentes.
Dê uma olhada na aplicação funcionando:
Pré-requisitos
- Instale o Visual Studio Code aqui;
- Instale a última versão da Angular CLI por aqui;
- Instale a última versão LTS do Node.js aqui.
Código fonte
Você pode acessar o código fonte pelo GitHub.
Crie a aplicação do Angular
Navegue até o diretório no qual deseja criar o seu projeto. Abra uma janela de comando e execute o comando mostrado abaixo:
ng new angular-forms-validation --routing=false --style=scss
Estamos especificando o comando para criar uma aplicação do Angular. A opção para criar o módulo de roteamento é definida como false
e a extensão de arquivos de estilização é definida como SCSS. Esse comando criará o projeto do Angular com o nome angular-forms-validation
.
Dirija-se ao diretório do novo projeto e abra-o no VS Code usando o conjunto de comandos abaixo.
cd angular-forms-validation
code .
Instale o Bootstrap
Execute o seguinte comando para instalar o Bootstrap:
npm install bootstrap --save
Adicione a seguinte definição de importação no arquivo styles.scss
:
@import "~bootstrap/dist/css/bootstrap.css";
Crie o serviço de validação
Execute o seguinte comando para criar um serviço:
ng g s services\customvalidation
Esse comando criará um subdiretório chamado services
, que possui dois arquivos dentro dele – customvalidation.service.ts
e customvalidation.service.spec.ts
. Abra customvalidation.service.ts
e insira o seguinte código dentro dele.
import { Injectable } from '@angular/core';
import { ValidatorFn, AbstractControl } from '@angular/forms';
import { FormGroup } from '@angular/forms';
@Injectable({
providedIn: 'root'
})
export class CustomvalidationService {
patternValidator(): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } => {
if (!control.value) {
return null;
}
const regex = new RegExp('^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9]).{8,}$');
const valid = regex.test(control.value);
return valid ? null : { invalidPassword: true };
};
}
MatchPassword(password: string, confirmPassword: string) {
return (formGroup: FormGroup) => {
const passwordControl = formGroup.controls[password];
const confirmPasswordControl = formGroup.controls[confirmPassword];
if (!passwordControl || !confirmPasswordControl) {
return null;
}
if (confirmPasswordControl.errors && !confirmPasswordControl.errors.passwordMismatch) {
return null;
}
if (passwordControl.value !== confirmPasswordControl.value) {
confirmPasswordControl.setErrors({ passwordMismatch: true });
} else {
confirmPasswordControl.setErrors(null);
}
}
}
userNameValidator(userControl: AbstractControl) {
return new Promise(resolve => {
setTimeout(() => {
if (this.validateUserName(userControl.value)) {
resolve({ userNameNotAvailable: true });
} else {
resolve(null);
}
}, 1000);
});
}
validateUserName(userName: string) {
const UserList = ['ankit', 'admin', 'user', 'superuser'];
return (UserList.indexOf(userName) > -1);
}
}
O método patternValidator
é usado para validar o padrão de senha no seu formulário. O parâmetro para esse método é do tipo AbstractControl
, que é uma classe base para oFormControl
.
Usaremos uma expressão regular para validar a senha. Ela verificará as seguintes quatro condições na senha:
- A senha deve ter, no mínimo, oito caracteres;
- Deve possuir pelo menos uma letra minúscula;
- Deve conter, no mínimo, uma letra maiúscula;
- Deve ter, ao menos, um número.
Se a senha falhar na verificação da regex, definiremos a propriedade invalidPassword
como sendo verdadeira (true).
O método MatchPassword
é usado para comparar as senhas em dois campos. Esse método receberá dois parâmetros do tipo string, os quais representam os nomes dos campos a serem comparados. Utilizaremos o FormControl
para esses dois campos e, em seguida, compararemos os valores contidos neles. Se os valores não corresponderem, definiremos a propriedade passwordMismatch
como verdadeira (true).
O método userNameValidator
é utilizado para verificar se o nome de usuário já foi utilizado ou não. Esse método receberá um parâmetro do tipo AbstractControl
.
Verificaremos se o valor deste campo está presente em um array estático, UserList
. Se o valor inserido pelo usuário já estiver presente, definiremos a propriedade userNameNotAvailable
como verdadeira (true).
Estamos utilizando a função setTimeout
para invocar essa verificação a cada dois segundos. Isso assegurará que o erro seja disparado após dois segundos a partir do momento em que o usuário parar de digitar no campo.
Por questão de simplicidade deste artigo, estamos fazendo o uso de um array estático para buscar a disponibilidade de nomes de usuário. Idealmente, essa deveria ser uma chamada de serviço ao servidor para procurar pelo valor em um banco de dados.
Crie o modelo de usuário
Crie uma pasta chamada models
dentro de src/app
. Adicione um novo arquivo dentro do diretório models
, chamado user.ts
. Insira o seguinte código dentro do arquivo user.ts
.
export class User {
public name: string;
public email: string;
public username: string;
public password: string;
public confirmPassword: string;
}
Crie diretivas customizadas
Criaremos diretivas customizadas para implementar validators customizados para o formulário orientado a templates.
Execute o comando exibido abaixo para criar a diretiva passwordPattern
.
ng g d directives\passwordPattern
Este comando criará uma pasta chamada directives
, que possui dois arquivos dentro de si – passwordPattern.directive.ts
e passwordPattern.directive.spec.ts
. Abra o passwordPattern.directive.ts
e insira o seguinte código dentro dele.
import { Directive } from '@angular/core';
import { NG_VALIDATORS, Validator, AbstractControl } from '@angular/forms';
import { CustomvalidationService } from '../services/customvalidation.service';
@Directive({
selector: '[appPasswordPattern]',
providers: [{ provide: NG_VALIDATORS, useExisting: PasswordPatternDirective, multi: true }]
})
export class PasswordPatternDirective implements Validator {
constructor(private customValidator: CustomvalidationService) { }
validate(control: AbstractControl): { [key: string]: any } | null {
return this.customValidator.patternValidator()(control);
}
}
Essa diretiva é usada para validar o padrão da senha. Implementaremos a interface Validator
na classe PasswordPatternDirective
. Sobrescreveremos o método validate
, o qual recebe um parâmetro do tipo AbstractControl
, que é o controle que desejamos validar. Em seguida, invocaremos o método patternValidator
do serviço.
Execute o comando mostrado abaixo para criar a diretiva matchPassword
:
ng g d directives\matchPassword
Abra o arquivo matchPassword.directive.ts
e insira o seguinte código dentro dele:
import { Directive, Input } from '@angular/core';
import { NG_VALIDATORS, Validator, ValidationErrors, FormGroup } from '@angular/forms';
import { CustomvalidationService } from '../services/customvalidation.service';
@Directive({
selector: '[appMatchPassword]',
providers: [{ provide: NG_VALIDATORS, useExisting: MatchPasswordDirective, multi: true }]
})
export class MatchPasswordDirective implements Validator {
@Input('appMatchPassword') MatchPassword: string[] = [];
constructor(private customValidator: CustomvalidationService) { }
validate(formGroup: FormGroup): ValidationErrors {
return this.customValidator.MatchPassword(this.MatchPassword[0], this.MatchPassword[1])(formGroup);
}
}
Essa diretiva é usada para validar se as senhas inseridas nos dois campos correspondem ou não. Essa diretiva receberá uma entrada do tipo array de strings, o qual contém os campos a serem comparados. Sobrescreveremos o método validate
e passaremos o parâmetro do tipo FormGroup
. Em seguida, invocaremos o método MatchPassword
do serviço.
Execute o comando abaixo para criar a diretiva validateUserName
:
ng g d directives\validateUserName
Abra o arquivo validateUserName.directive.ts
e introduza o seguinte código nele:
import { Directive, forwardRef } from '@angular/core';
import { Validator, AbstractControl, NG_ASYNC_VALIDATORS } from '@angular/forms';
import { CustomvalidationService } from '../services/customvalidation.service';
import { Observable } from 'rxjs';
@Directive({
selector: '[appValidateUserName]',
providers: [{ provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => ValidateUserNameDirective), multi: true }]
})
export class ValidateUserNameDirective implements Validator {
constructor(private customValidator: CustomvalidationService) { }
validate(control: AbstractControl): Promise<{ [key: string]: any }> | Observable<{ [key: string]: any }> {
return this.customValidator.userNameValidator(control);
}
}
Essa diretiva é usada para validar a disponibilidade do nome de usuário. Sobrescreveremos o método validate
e passaremos um parâmetro do tipo AbstractControl
. Em seguida, invocaremos o método userNameValidator
do serviço. Esse método retornará uma promise.
Crie o componente do formulário orientado a templates
Execute o comando apresentado abaixo para criar o componente do formulário orientado a templates:
ng g c template-driven-form
Abra o arquivo template-driven-form.component.ts
e insira o seguinte código nele:
import { Component } from '@angular/core';
import { User } from '../models/user';
@Component({
selector: 'app-template-driven-form',
templateUrl: './template-driven-form.component.html',
styleUrls: ['./template-driven-form.component.scss']
})
export class TemplateDrivenFormComponent {
userModal = new User();
constructor() { }
onSubmit() {
alert('Form Submitted succesfully!!!\n Check the values in browser console.');
console.table(this.userModal);
}
}
Criamos um objeto userModal
do tipo User
. Vincularemos os campos do formulário com a propriedade desse objeto. O método onSubmit
mostrará a mensagem de sucesso na tela e exibirá os conteúdos do formulário no console.
Abra o arquivo template-driven-form.component.html
e introduza o seguinte código nele:
<div class="container">
<div class="row">
<div class="col-md-8 mx-auto">
<div class="card">
<div class="card-header">
<h3>Angular Template-driven Form</h3>
</div>
<div class="card-body">
<form class="form" #registerForm="ngForm" [appMatchPassword]="['password', 'confirmPassword']"
(ngSubmit)="registerForm.form.valid && onSubmit()" novalidate>
<div class=" form-group">
<label>Name</label>
<input type="text" class="form-control" [(ngModel)]="userModal.name" name="name"
#name="ngModel" required>
<span class="text-danger"
*ngIf="(name.touched || registerForm.submitted) && name.errors?.required">
Name is required
</span>
</div>
<div class="form-group">
<label>Email</label>
<input type="text" class="form-control" [(ngModel)]="userModal.email" name="email"
#email="ngModel" required email>
<span class="text-danger"
*ngIf="(email.touched || registerForm.submitted) && email.errors?.required">
Email is required
</span>
<span class="text-danger" *ngIf="email.touched && email.errors?.email">
Enter a valid email address
</span>
</div>
<div class="form-group">
<label>User Name</label>
<input type="text" class="form-control" [(ngModel)]="userModal.username" name="username"
#username="ngModel" appValidateUserName required>
<span class="text-danger"
*ngIf="(username.touched || registerForm.submitted) && username.errors?.required">
User Name is required
</span>
<span class="text-danger" *ngIf="username.touched && username.errors?.userNameNotAvailable">
User Name not available
</span>
</div>
<div class="form-group">
<label>Password</label>
<input type="password" class="form-control" [(ngModel)]="userModal.password" name="password"
#password="ngModel" appPasswordPattern required>
<span class="text-danger"
*ngIf="(password.touched || registerForm.submitted) && password.errors?.required">
Password is required
</span>
<span class="text-danger" *ngIf="password.touched && password.errors?.invalidPassword">
Password should have minimum 8 characters, at least 1 uppercase letter, 1 lowercase
letter and 1 number
</span>
</div>
<div class="form-group">
<label>Confirm Password</label>
<input type="password" class="form-control" [(ngModel)]="userModal.confirmPassword"
name="confirmPassword" #confirmPassword="ngModel" required>
<span class="text-danger"
*ngIf="(confirmPassword.touched || registerForm.submitted) && confirmPassword.errors?.required">
Confirm Password is required
</span>
<span class="text-danger"
*ngIf="confirmPassword.touched && confirmPassword.errors?.passwordMismatch">
Passwords doesnot match
</span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success">Register</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
Criaremos um formulário orientado a templates e usaremos o card do Bootstrap para estilização. O card-header
conterá um título, enquanto o card-body
terá os campos do formulário.
Usaremos a diretiva appMatchPassword
em nosso formulário e passaremos para validação os campos de senha e confirmPassword
. A propriedade ngModel
é usada para vincular o controle do formulário ao modelo.
Para validar a disponibilidade do nome de usuário, usaremos a diretiva appValidateUserName
no campo username
. Do mesmo modo, utilizaremos a diretiva appPasswordPattern
no campo de senha para validar o padrão da senha.
Verificaremos os erros nos controles do formulário e, então, exibiremos na tela a mensagem de erro de validação apropriada.
Crie um componente nav-bar
Execute o comando abaixo para criar um componente nav-bar
:
ng g c nav-bar
Abra o arquivo nav-bar.component.html
e insira nele o seguinte código:
<nav class="navbar navbar-expand-sm navbar-dark bg-dark fixed-top">
<a class="navbar-brand" [routerLink]='["/"]'>Form Validation Demo</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" [routerLink]='["/template-form"]'>Template Form</a>
</li>
</ul>
</div>
</nav>
Aqui, estamos adicionando o link de navegação para o componente de formulário orientado a templates.
Atualize o componente app
Abra o arquivo app.component.html
e introduza nele o código a seguir:
<app-nav-bar></app-nav-bar>
<div class="container">
<router-outlet></router-outlet>
</div>
Atualize o módulo de app
Importaremos o módulo de formulários e, também, configuraremos o roteamento para nossa aplicação no módulo do app
. Adicione o seguinte código no arquivo app.module.ts
. Você pode consultar o GitHub para obter o código fonte completo deste arquivo:
import { RouterModule } from '@angular/router';
import { FormsModule } from '@angular/forms';
@NgModule({
...
imports: [
...
FormsModule,
RouterModule.forRoot([
{ path: '', component: TemplateDrivenFormComponent },
{ path: 'template-form', component: TemplateDrivenFormComponent }
]),
],
})
Execução de demonstração
Use o comando a seguir para iniciar o servidor da web:
ng serve -o
Este comando iniciará a aplicação em seu navegador padrão no http://localhost:4200/
. Você pode realizar todas as validações de formulário que discutimos aqui.
Resumo
Criamos um formulário de registro de usuário de exemplo usando a abordagem de formulário orientado a templates no Angular. Implementamos no formulário as validações nativas, assim como as validações customizadas. A biblioteca do Bootstrap foi usada para estilização do formulário.
Acesse o código-fonte pelo GitHub e explore o repositório para ter um melhor entendimento.
Veja também (recursos em inglês)
- Reactive Form Validation In Angular
- Localization In Angular Using i18n Tools
- Policy-Based Authorization In Angular Using JWT
- ASP.NET Core – Using Highcharts With Angular 5
- ASP.NET Core – CRUD Using Angular And Entity Framework Core
Você pode encontrar este artigo em inglês, Template-Driven Form Validation In Angular, e outros artigos semelhantes no blog do autor.