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-validserá aplicada. Se o valor for inválido, a classeng-invalidserá 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.
