web-dev-qa-db-fra.com

Angular2 - FormControl Validation sur flou

Je cherche à ajouter une validation de base du courrier électronique pour vérifier que l'utilisateur a entré une adresse électronique correcte. Actuellement, à l'aide de la méthode ci-dessous, la validation est mise à jour au fur et à mesure que l'utilisateur tape, ce qui semble étrange quand il y a une erreur après la saisie d'un caractère.

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

Je me demandais s'il était possible de déclencher la validation sur le flou du champ, comme dans angularJS avec:

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

Je suis conscient de l'option de flou du champ de saisie au sein du code HTML, mais cela ne met pas mon contrôle en erreur sauf s'il existe un moyen de le placer dans un état d'erreur.

Quelqu'un pourrait-il m'aider à me diriger dans la bonne direction?

Merci.

Edit: Je cherche une solution angular2, pas une solution angularJS.

38
Reece Thompson

EDIT 2

Comme Alex et le documentation officielle dit, version angulaire 5.0.0 a une nouvelle option pour votre ngModel updateOn: 'blur'

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

Vous pouvez également utiliser d'autres options de mise à jour: change (valeur par défaut), blur, submit.


Original

J'utilise la directive où supprimer toute la validation sur le focus et la renvoyer après l'événement de flou. Il est basé sur la réponse de Cristian Deschamps.

Je mets à jour la validité uniquement sur le flou. Ainsi, si la valeur était invalide avant le focus, elle ne le serait plus après. Mais si vous commencez la saisie, la validité sera mise à jour.

Pour certaines raisons, l'ordre d'effacement a du sens, j'efface donc d'abord les validateurs asynchrones.

Toute suggestion fournie sera utile =)

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

Aussi, s'il vous plaît restez à l'écoute sur ce fil angulaire 2 github à propos de la validation onBlur


EDIT 1

Il y a un autre problème - si je clique simplement sur le champ et après clic, la validation sera appelée. Si vous avez une notification à ce sujet (ou des appels de serveur), il apparaît chaque fois que vous le faites. Ainsi, vous pouvez ajouter la propriété wasChanged et l'utiliser comme ceci:

    @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();
        }
    }
49
Alex Shestakov

À partir de Angular v 5.0.0 c’est maintenant possible en marquant updateOn: 'blur' au contrôle de formulaire.

Cela signifie également que valueChanges ne se déclenche pas pour ce contrôle de formulaire jusqu'à ce que l'événement de flou se produise. Voici un exemple avec minlength avec required:

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

get username() {
  return this.form.get('username');
}

Dans le modèle, vous voudrez indiquer que le message de validation ne sera pas affiché à moins que le champ ne soit touched:

<div *ngIf="username.hasError('minlength') || username.hasError('required') 
              && username.touched">Required and minlength 6!
</div>

DÉMO

P.S Si nécessaire, vous pouvez également le marquer sur tout le formulaire, pas seulement sur un contrôle de formulaire spécifique

13
AJT_82

Trouver un moyen, dans rc6.

1- Créer une directive: 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);
    }
}

Ensuite, dans votre modèle HTML, ajoutez simplement la directive à votre formulaire, mon exemple utilise le modèle ReactiveFormsModule.

Ajoutez ensuite ceci à votre message d'erreur:

<input type="text" formControlName="full_name" validate-onblur />

<span *ngIf="formAccountDetails.controls.full_name.touched && !formAccountDetails.controls.full_name.valid && !formAccountDetails.controls.full_name.pristine" class="errors">
        ...
</span>
8
Cristian Deschamps

Quelque chose comme ceci: Utilisez la propriété touché du ngControl objet.

 <div class="form-group" [class.has-error]="!name.valid && name.touched">
        <label for="name">Name</label>
        <input #name="ngForm" ngControl="name" name="name" type="text" class="form-control" required>
 </div>
7
tomasz

J'ai un peu amélioré la solution Alex Shestakov, qui fonctionnait déjà auparavant, afin d'éviter de définir l'état de contrôle sur valide lorsque sa valeur change tout en maintenant le focus.

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

De cette façon, le contrôle restera en attente tant qu'il gardera le focus. Cela aidera à éviter le cas où un contrôle est invalide avant d'avoir le focus, puis dès que l'utilisateur commence à taper dessus, il est marqué comme valide, avant que l'événement de flou ne se produise, lorsque les validateurs sont redéfinis et la validité réelle de le contrôle doit être déterminé.

4