web-dev-qa-db-fra.com

Validation entre champs dans Angular2

Je crée une application côté client Angular2. Je travaille actuellement sur les composants d'appartenance et l'intégration des composants côté client avec MVC6 vNext Identity v3. J'ai écrit des validateurs de mot de passe Angular2 personnalisés comme suit:

needsCapitalLetter(ctrl: Control): {[s: string]: boolean} {
    if(!ctrl.value.match(/[A-Z]/))
        return {'needsCapitalLetter': true}

    return null;
}

needsLowerLetter(ctrl: Control): {[s: string]: boolean} {
    if(!ctrl.value.match(/[a-z]/))
        return {'needsLowerLetter': true}

    return null;            
}

needsNumber(ctrl: Control): {[s: string]: boolean} {
    if(!ctrl.value.match(/\d/))
        return {'needsNumber': true}

    return null;            
}

needsSpecialCharacter(ctrl: Control): {[s: string]: boolean} {
    if(!ctrl.value.match(/[^a-zA-Z\d]/))
        return {'needsSpecialCharacter': true}

    return null;            
}

Cela fonctionne très bien, et j'aime Angular2, mais maintenant j'essaie d'écrire un validateur qui vérifie que le "Confirm Password" est égal au "Password". Pour ce faire, je dois pouvoir valider un champ par rapport à l'autre. Je peux facilement le faire au niveau des composants, et vérifier simplement le flou, ou soumettre, ou un certain nombre d'autres façons, mais cela contourne le système de validation Angular2 ngForm. Je voudrais bien comprendre comment écrire un validateur Angular2 pour un champ qui peut vérifier la valeur d'un autre champ, en passant le nom de l'autre champ ou quelque chose de proche. Il semble que cela devrait être une capacité car cela serait une nécessité dans presque toutes les interfaces utilisateur d'applications complexes.

19
Coaden

Vous devez affecter un validateur personnalisé à un groupe de formulaires complet pour l'implémenter. Quelque chose comme ça:

this.form = this.fb.group({
  name:  ['', Validators.required],
  email: ['', Validators.required]
  matchingPasswords: this.fb.group({
    password:        ['', Validators.required],
    confirmPassword: ['', Validators.required]
  }, {validator: this.matchValidator})  <--------
});

De cette façon, vous aurez accès à tous les contrôles du groupe et pas à un seul ... Vous pouvez y accéder en utilisant la propriété controls du FormGroup. Le FormGroup est fourni lorsque la validation est déclenchée. Par exemple:

matchValidator(group: FormGroup) {
  var valid = false;

  for (name in group.controls) {
    var val = group.controls[name].value
    (...)
  }

  if (valid) {
    return null;
  }

  return {
    mismatch: true
  };
}

Voir cette question pour plus de détails:

28
Thierry Templier

Vous pouvez également utiliser un validateur de directive personnalisé pour comparer les champs.

Dans votre html:

<div>
    <label>Password</label>
    <input type="password" name="password" [ngModel]="user.password" 
        required #password="ngModel">
    <small [hidden]="password.valid || (password.pristine && !f.submitted)">
        Password is required
    </small>
</div>
<div>
    <label>Retype password</label>
    <input type="password" name="confirmPassword" [ngModel]="user.confirmPassword" 
        required validateEqual="password" #confirmPassword="ngModel">
    <small [hidden]="confirmPassword.valid ||  (confirmPassword.pristine && !f.submitted)">
        Password mismatch
    </small>
</div>

Et votre directive:

import { Directive, forwardRef, Attribute } from '@angular/core';
import { Validator, AbstractControl, NG_VALIDATORS } from '@angular/forms';
@Directive({
    selector: '[validateEqual][formControlName],[validateEqual][formControl],[validateEqual][ngModel]',
    providers: [
        { provide: NG_VALIDATORS, useExisting: forwardRef(() => EqualValidator), multi: true }
    ]
})
export class EqualValidator implements Validator {
    constructor( @Attribute('validateEqual') public validateEqual: string) {}

    validate(c: AbstractControl): { [key: string]: any } {
        // self value (e.g. retype password)
        let v = c.value;

        // control value (e.g. password)
        let e = c.root.get(this.validateEqual);

        // value not equal
        if (e && v !== e.value) return {
            validateEqual: false
        }
        return null;
    }
}

Voici la solution complète dans plunkr:

https://plnkr.co/edit/KgjSTj7VqbWMnRdYZdxM?p=preview

13
Diego Unanue

Je ne l'ai pas fait moi-même, mais vous pouvez créer ControlGroup avec deux champs de mot de passe et le valider. Les contrôles ont .valueChanges propriété qui est observable et vous pouvez les combiner et vérifier leur égalité.

Victor Savkin parle brièvement de ce cas précis sur Angular Air in cet épisode

4
Sasxa