Custom Validators com ReactiveForms - síncronos e assíncronos!
Felipe Carvalho
Posted on July 19, 2019
Introdução
ReactiveForms nos permite criar validações customizadas que alterarão o estado do formulário da mesma forma que as validações comuns o fazem, sejam elas síncronas ou assíncronas.
Demonstrarei como criá-las baseado no exemplo que criei no post anterior: Introdução aos Reactive Forms!
Validação síncrona
Foi criada uma função de validação chamada senhasProibidas que recebe um FormControl, que será o controle onde aplicaremos a função.
Ela basicamente está validando se a senha é 12345 e retornando uma estrutura que o formulário identificará como erro. Mais abaixo explicarei ponto a ponto.
senhasProibidas(control: FormControl): { [s: string]: boolean } {
if (control.value === "12345") {
return { 'senhaProibida': true };
} else {
return null;
}
}
Criado o método de validação, ele pode ser aplicado no controle que criamos ao instanciar o formulário. Repare que passamos a função senhasProibidas no mesmo array que as validações comuns.
this.usuarioForm = new FormGroup({
"nome": new FormControl("João", [Validators.required]),
"endereco": new FormControl(),
"acesso": new FormGroup({
"email": new FormControl(null, [Validators.required, Validators.email]),
"senha": new FormControl(null, [Validators.required, Validators.minLength(3), this.senhasProibidas])
}),
"genero": new FormControl("M", [Validators.required]),
"estadoCivil": new FormControl(null, [Validators.required]),
"telefones": new FormArray([])
});
Mas o que é esse retorno { [s: string]: boolean }?!
Quando definimos uma propriedade entre [colchetes], criamos a possibilidade de informar qualquer chave para aquele objeto, isso também pode ser utilizado como um "truque" para permitir uma classe receber qualquer propriedade, sem que o typescript acuse erro. Ao invés de criar a estrutura do objeto no retorno, poderíamos também criar uma classe, sem problema algum:
export class ChaveValor {
[s: string]: boolean;
}
Porque retornamos null quando o valor é válido?
Quando detectamos erro, retornamos um chave-valor "senhaProibida": true, conforme o que foi estabelecido na declaração da função.
Porém, quando o valor está OK, a função retorna null ao invés de 'senhaProibida': false. É necessário retornar null pois é assim que o Angular interpreta que não houve erros naquela validação.
if (control.value === "12345") {
return { 'senhaProibida': true };
} else {
return null;
}
Exibi a estrutura criada com console.log para visualizarmos como ocorre a validação.
Quando retornamos null, temos o seguinte estado:
Se temos key: true, o Angular já identifica que o controle está inválido:
Agora, se temos key: false, o controle está detectando que há algo dentro de errors e, apesar de querermos sinalizar que o controle está válido, ao passar algo para a propriedade, o Angular considera o controle inválido:
Validação assíncrona
Foi criada uma função que irá retornar uma chave e um boolean quando algum erro for detectado e nulo quando não houver erro, porém, este retorno deve ocorrer em forma de Observable (poderia ser Promise, também).
Na função, uma requisição ao servidor foi simulada com um setTimeout para verificar se o e-mail informado é email@proibido.com:
emailsProibidos(control): Observable<{ [s: string]: boolean }> {
return new Observable<{ [s: string]: boolean }>(observer => {
setTimeout(() => {
if (control.value == "email@proibido.com") {
observer.next({ "emailProibido": true });
} else {
observer.next(null);
}
observer.complete();
}, 2000);
});
}
Após criar o método de validação, ele pode ser aplicado no controle que criamos ao instanciar o formulário.
this.usuarioForm = new FormGroup({
"nome": new FormControl("João", [Validators.required]),
"endereco": new FormControl(),
"acesso": new FormGroup({
"email": new FormControl(null, [Validators.required, Validators.email], this.emailsProibidos),
"senha": new FormControl(null, [Validators.required, Validators.minLength(3), this.senhasProibidas])
}),
"genero": new FormControl("M", [Validators.required]),
"estadoCivil": new FormControl(null, [Validators.required]),
"telefones": new FormArray([])
});
No controle email, o novo validador foi adicionado após os validadores "comuns". O terceiro parâmetro do FormControl recebe os validadores assíncronos:
Agora, é importante observar que o formulário, enquanto espera a validação ser concluída, assume o estado PENDING. Relacionado a esse comportamento, no post anterior (mencionado no início deste), destaquei a importância de usar !usuarioForm.valid quando for desabilitar o botão submit.
Vamos ver funcionando?
Informe a senha 12345 e o e-mail email@proibido.com e veja a validação atuando.
Recomendo abrir em uma nova aba e você pode fazer isso clicando aqui.
Posted on July 19, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.