Angular2 – validação de FormControl no borrão

Eu estou olhando para adicionar alguma validação básica de e-mail para verificar se o usuário colocou em um endereço de e-mail correto. Atualmente, usando o método abaixo, a validação é atualizada conforme o usuário digita, o que parece estranho quando ocorre um erro após inserir um caractere.

validEmail(c: Control){ if(!c.value.match('[a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?')){ return { validEmail: true }; } return null; } ctrlEmailAddress: Control = new Control('', Validators.compose([ Validators.required, this.validEmail])); 

Eu queria saber se é possível acionar a validação no borrão do campo, como no angularJS com:

 ng-model-options="{ updateOn: 'blur' }" 

Estou ciente da opção de desfoque no campo de input dentro do html, mas isso não coloca meu controle em erro, a menos que haja uma maneira de colocar o controle em um estado de erro.

Alguém poderia me ajudar na direção certa?

Obrigado.

Edit: Estou à procura de uma solução angular2, não uma solução angularJS.

EDIT 2

Como Alex e a documentação oficial dizem, o Angular versão 5.0.0 tem uma nova opção para o seu ngModel updateOn: 'blur'

 this.email = new FormControl(null, { validators: Validators.required, updateOn: 'blur' }); 

Além disso, você pode usar outras opções de atualização: change (padrão), blur , submit .


Original

Eu uso diretiva onde remover validação inteira em foco e devolvê-lo de volta após o evento de desfoque. É baseado na resposta de Cristian Deschamps.

Eu atualizo a validade apenas no desfoque, portanto, se o valor for inválido antes do foco, ele será inválido depois. Mas se você iniciar a input, a validade será atualizada.

Por alguns motivos, a ordem de limpeza faz sentido, portanto, limpe os validadores asynchronouss primeiro.

Qualquer sugestão fornecida será útil =)

 import { Directive } from '@angular/core'; import { NgControl } from '@angular/forms'; @Directive({ selector: '[validate-onblur]', host: { '(focus)': 'onFocus($event)', '(blur)': 'onBlur($event)' } }) export class ValidateOnBlurDirective { private validators: any; private asyncValidators: any; constructor(public formControl: NgControl) { } onFocus($event) { this.validators = this.formControl.control.validator; this.asyncValidators = this.formControl.control.asyncValidator; this.formControl.control.clearAsyncValidators(); this.formControl.control.clearValidators(); } onBlur($event) { this.formControl.control.setAsyncValidators(this.asyncValidators); this.formControl.control.setValidators(this.validators); this.formControl.control.updateValueAndValidity(); } } 

Além disso, por favor, fique atento neste tópico Angular 2 github sobre validação onBlur


EDITAR 1

Há outro problema – se apenas clicar no campo e depois de clicar – a validação será chamada. Se você tiver alguma notificação sobre isso (ou chamadas do servidor) – ele aparecerá toda vez que você fizer isso. Então você pode adicionar a propriedade wasChanged e usá-la assim:

  @Directive({ selector: '[validate-onblur]', host: { '(focus)': 'onFocus($event)', '(blur)': 'onBlur($event)', '(keyup)': 'onKeyup($event)', '(change)': 'onChange($event)', '(ngModelChange)': 'onNgModelChange($event)' } }) export class ValidationOnBlurDirective { private validators: any; private asyncValidators: any; private wasChanged: any; constructor(public formControl: NgControl) { } onFocus($event) { this.wasChanged = false; this.validators = this.formControl.control.validator; this.asyncValidators = this.formControl.control.asyncValidator; this.formControl.control.clearAsyncValidators(); this.formControl.control.clearValidators(); } onKeyup($event) { this.wasChanged = true; // keyboard change } onChange($event) { this.wasChanged = true; // copypaste change } onNgModelChange($event) { this.wasChanged = true; // ng-value change } onBlur($event) { this.formControl.control.setAsyncValidators(this.asyncValidators); this.formControl.control.setValidators(this.validators); if (this.wasChanged) this.formControl.control.updateValueAndValidity(); } } 

A partir do Angular v 5.0.0 isso agora é possível marcando updateOn: 'blur' para o controle de formulário.

Isso também significa que valueChanges não é triggersdo para esse controle de formulário até que ocorra o evento de desfoque. Aqui está um exemplo com minlength junto com required :

 this.form = new FormGroup({ username: new FormControl('', { validators: [Validators.required, Validators.minLength(6)], updateOn: 'blur'} ) }) get username() { return this.form.get('username'); } 

No modelo, você deseja marcar que a mensagem de validação não será exibida, a menos que o campo seja touched :

 
Required and minlength 6!

DEMO

PS Se necessário, você também pode marcar isso em todo o formulário, não apenas em um controle de formulário específico

Algo como isto: Use propriedade tocada do object ngControl .

  

Encontrei um jeito, no rc6.

1- Crie uma diretiva: validate-onblur.directive.ts

 @Directive({ selector: '[validate-onblur]', host: { '(focus)': 'onFocus($event)', '(blur)': 'onBlur($event)' } }) export class ValidateOnBlurDirective { constructor(public formControl: NgControl) { } onFocus($event) { this.formControl.control.markAsUntouched(false); } onBlur($event) { this.formControl.control.markAsTouched(true); } } 

Em seguida, no seu modelo html basta adicionar a diretiva ao seu formulário, o meu exemplo use o modelo ReactiveFormsModule.

Em seguida, adicione isso à sua mensagem de erro:

   ...  

Eu melhorei um pouco a solução de Alex Shestakov, que já funcionou pelo caminho, a fim de evitar que o estado de controle seja válido quando seu valor muda enquanto mantém o foco.

 @Directive({ selector: '[validate-onblur]', host: { '(focus)': 'onFocus($event)', '(blur)' : 'onBlur($event)' } }) export class ValidateOnBlurDirective { private validators: any; private asyncValidators: any; private hasFocus = false; constructor(public formControl: NgControl) { } onFocus($event) { this.hasFocus = true; this.validators = this.formControl.control.validator; this.asyncValidators = this.formControl.control.asyncValidator; this.formControl.control.clearAsyncValidators(); this.formControl.control.clearValidators(); this.formControl.control.valueChanges .filter(() => this.hasFocus) .subscribe(() => this.formControl.control.markAsPending()); } onBlur($event) { this.hasFocus = false; this.formControl.control.setAsyncValidators(this.asyncValidators); this.formControl.control.setValidators(this.validators); this.formControl.control.updateValueAndValidity(); } } 

Desta forma, o controle permanecerá no estado pendente, desde que mantenha o foco. Isso ajudará a evitar o caso quando um controle é inválido antes de obter o foco e, assim que o usuário começa a digitar, ele é marcado como válido, antes do evento de desfoque acontecer, quando os validadores são configurados novamente e a validade real de o controle deve ser determinado.

Você pode

 [class.has-error]="!form.controls['ctrlEmailAddress'].valid" 

Isso adicionará a class has-error assim que você alterar o modelo. Então, basicamente, você não precisa desfocar para ter a verificação de validação.