Estado do Modelo e Validação em Angular
Estado do Modelo
A aplicação da diretiva ngModel
não apenas estabelece a vinculação de dados, mas também permite o rastreamento do estado de um elemento de entrada. Para definir o estado, o Angular aplica classes CSS especiais aos elementos de entrada:
- Se o elemento de entrada ainda não recebeu foco, a classe
ng-untouched
é aplicada. Se o campo de entrada já tiver recebido foco, a classeng-touched
é aplicada. O recebimento de foco não precisa ser acompanhado por uma alteração no valor desse campo. - Se o valor original do campo de entrada foi alterado, a classe
ng-dirty
é aplicada. Se o valor não foi alterado desde o carregamento da página, a classeng-pristine
é aplicada ao elemento de entrada. - Se o valor no campo de entrada for válido, a classe
ng-valid
será aplicada. Se o valor for inválido, a classeng-invalid
será aplicada.
Por exemplo, ao carregar uma página web com o seguinte elemento de entrada:
<input class="form-control" name="username" [(ngModel)]="username" />
Será gerada a seguinte marcação HTML:
<input class="form-control ng-untouched ng-pristine ng-valid" name="username" ng-reflect-name="username" />
Validação
Antes de enviar o formulário, é necessário garantir que ele contenha valores corretos. Para essa verificação, é utilizado o mecanismo de validação. No Angular, é possível usar a validação HTML5, aplicada por meio de atributos:
required
: exige a inserção de um valor.pattern
: define uma expressão regular que os dados inseridos devem seguir.
Para usar a validação, definimos o seguinte componente:
import { Component } from "@angular/core";
import { FormsModule } from "@angular/forms";
class User {
constructor(public name: string, public email: string, public phone: string) {}
}
@Component({
selector: "my-app",
standalone: true,
imports: [FormsModule],
styles: `
.alert { color: red; }
div { margin: 5px 0; }
`,
template: `<div>
<div>
<label>Nome</label><br />
<input name="name" [(ngModel)]="user.name" #name="ngModel" required />
<div [hidden]="name.valid || name.untouched" class="alert">Nome não informado</div>
</div>
<div>
<label>Email</label><br />
<input name="email" [(ngModel)]="user.email" #email="ngModel" required pattern="^[a-zA-Z0-9.!#$%&’*+/=?^_{|}~-]+@[a-zA-Z0-9-]+(?:.[a-zA-Z0-9-]+)*$" />
<div [hidden]="email.valid || email.untouched" class="alert">Email inválido</div>
</div>
<div>
<label>Telefone</label><br />
<input name="phone" [(ngModel)]="user.phone" #phone="ngModel" required pattern="[0-9]{11}" />
<div [hidden]="phone.valid || phone.untouched" class="alert">Telefone inválido</div>
</div>
<button (click)="addUser()">Adicionar</button>
</div>`,
})
export class AppComponent {
user: User = new User("", "", "");
addUser() {
console.log(this.user);
}
}
Neste exemplo, definimos uma classe User
para trabalhar com os dados. Cada campo de entrada está vinculado a uma propriedade do objeto User
. Além disso, são definidos requisitos de validação para cada campo usando os atributos required
e pattern
. Ao lado de cada campo de entrada, há um bloco especial para exibir mensagens de erro:
<div [hidden]="email.valid || email.untouched" class="alert">Email inválido</div>
Aqui, estamos verificando a validade do campo de email. Para isso, utilizamos a variável email
, que representa o objeto NgModel
associado ao campo de entrada (#email="ngModel")
, e verificamos suas propriedades valid
e untouched
. Se pelo menos uma dessas propriedades for true
, o atributo hidden
também será true
, e o bloco div
será oculto. Ou seja, se o campo de email for válido, o bloco será escondido. Caso contrário, uma mensagem de erro será exibida.
Apesar dos erros de validação, ainda podemos clicar no botão e executar o método addUser()
, que processará os dados inseridos. No entanto, como os dados são inválidos, o processamento será inútil. Nessa situação, podemos desativar o botão usando a verificação de validade dos campos. Vamos modificar o código do botão da seguinte forma:
<button [disabled]="name.invalid || email.invalid || phone.invalid" (click)="addUser()">Adicionar</button>
Com a expressão [disabled]="name.invalid || email.invalid || phone.invalid"
, o atributo disabled
será definido como true
, ou seja, o botão será desativado se pelo menos um dos campos estiver inválido.
Estiliando Campos Inválidos
Além de exibir mensagens de erro, é possível estilizar campos inválidos. Para isso, podemos usar a classe ng-invalid
aplicada a campos inválidos. Vamos adicionar estilos para campos inválidos:
import { Component } from "@angular/core";
import { FormsModule } from "@angular/forms";
class User {
constructor(public name: string, public email: string, public phone: string) {}
}
@Component({
selector: "my-app",
standalone: true,
imports: [FormsModule],
styles: `
.alert { color: red; }
div { margin: 5px 0; }
input.ng-touched.ng-invalid { border: solid red 2px; }
input.ng-touched.ng-valid { border: solid green 2px; }
`,
template: `<div>
<div>
<label>Nome</label><br />
<input name="name" [(ngModel)]="user.name" #name="ngModel" required />
</div>
<div>
<label>Email</label><br />
<input name="email" type="email" [(ngModel)]="user.email" #email="ngModel" required email />
</div>
<div>
<label>Telefone</label><br />
<input name="phone" [(ngModel)]="user.phone" #phone="ngModel" required pattern="[0-9]{11}" />
</div>
<button [disabled]="name.invalid || email.invalid || phone.invalid" (click)="addUser()">Adicionar</button>
</div>`,
})
export class AppComponent {
user: User = new User("", "", "");
addUser() {
console.log(this.user);
}
}
Neste exemplo, adicionamos estilos para campos inválidos e válidos. Para isso, usamos as classes ng-touched
, ng-invalid
e ng-valid
. Se um campo de entrada for inválido, ele será destacado com uma borda vermelha. Se for válido, a borda será verde.