DEV Community

Cover image for Custom Validators com ReactiveForms - síncronos e assíncronos!
Felipe Carvalho
Felipe Carvalho

Posted on

Custom Validators com ReactiveForms - síncronos e assíncronos!

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;
  }
}


Enter fullscreen mode Exit fullscreen mode

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([])
});


Enter fullscreen mode Exit fullscreen mode

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;
}


Enter fullscreen mode Exit fullscreen mode

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;
  }


Enter fullscreen mode Exit fullscreen mode

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);
  });
}



Enter fullscreen mode Exit fullscreen mode

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([])
});


Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)