web-dev-qa-db-fra.com

Validateur personnalisé sur le formulaire réactif pour le mot de passe et confirmer la correspondance du mot de passe en obtenant des paramètres non définis dans Angular 4

J'essaie d'implémenter un validateur personnalisé pour vérifier si le mot de passe et le mot de passe de confirmation sont égaux. Le problème est que le validateur obtient un mot de passe indéfini et les paramètres confirmésPassword. Comment puis-je faire ce travail. La fonction fonctionne car si je change la condition === au lieu de! ==, l'erreur est renvoyée correctement lorsque les champs sont identiques Est-ce que quelqu'un sait quelle est l'erreur ici?

signup.component.html

<div class="col-md-7 col-md-offset-1 col-sm-7">
  <div class="block">
    <div class="well">
        <form (onSubmit)="onSubmit()" [formGroup]="signUpForm">
          <div class="form-group">
            <label for="username" class="control-label">Nombre de usuario:</label>
            <input type="text" class="form-control" formControlName="username"  title="Please enter your username" placeholder="username">
            <p class="help-block" *ngIf="signUpForm.get('username').hasError('required') && signUpForm.get('username').touched">El nombre de usuario es obligatorio</p>
            <p class="help-block" *ngIf="signUpForm.get('username').hasError('minlength') && signUpForm.get('username').touched">El nombre de usuario debe tener al menos 6 caracteres</p>
            <p class="help-block" *ngIf="signUpForm.get('username').hasError('maxlength') && signUpForm.get('username').touched">El nombre de usuario debe tener menos de 15 caracteres</p>
          </div>
          <div class="form-group">
            <label for="email" class="control-label">E-mail:</label>
            <input class="form-control" formControlName="email" title="Please enter your email" placeholder="[email protected]">
            <p class="help-block" *ngIf="signUpForm.get('email').hasError('required') && signUpForm.get('email').touched">La dirección de email es obligatoria</p>
            <p class="help-block" *ngIf="signUpForm.get('email').hasError('email') && signUpForm.get('email').touched">Debe ingresar una dirección de correo válida</p>
          </div>
          <div class="form-group">
            <label for="password" class="control-label">Contraseña:</label>
            <input type="password" class="form-control" formControlName="password" title="Please enter your password" [(ngModel)]="password">
            <p class="help-block" *ngIf="signUpForm.get('password').hasError('required') && signUpForm.get('password').touched">Debe ingresar una contraseña</p>
          </div>
          <div class="form-group">
            <label for="confirmedPassword" class="control-label">Confirmar Contraseña:</label>
            <input type="password" class="form-control" formControlName="confirmedPassword"  title="Please re-enter your password" [(ngModel)]="confirmedPassword">
            <p class="help-block" *ngIf="signUpForm.get('confirmedPassword').hasError('required') && signUpForm.get('confirmedPassword').touched">La confirmación de contraseña no puede estar vacía</p>
            <p class="help-block" *ngIf="signUpForm.get('confirmedPassword').hasError('passwordMismatch') && signUpForm.get('confirmedPassword').touched">Las contraseñas no coinciden</p>
          </div>
          <button type="submit" class="btn btn-success" [disabled]="!signUpForm.valid">Registrarse</button>
          <a routerLink="/signin" class="btn btn-default" style="">Ya tenes usuario? Logueate</a> {{ creationMessage }}
        </form>

      </div>

  </div>
</div>

signup.component.ts

import { Component, OnInit, ViewChild, Input } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { CustomValidators } from '../../shared/custom-validators';
import { Observable } from 'rxjs/Observable';

@Component({
  selector: 'app-signup',
  templateUrl: './signup.component.html',
  styleUrls: ['./signup.component.sass']
})
export class SignupComponent implements OnInit {

  signUpForm: FormGroup;
  user = {
    username: '',
    email: '',
    password: ''
  };
  submitted = false;
  @Input() password='';
  @Input() confirmedPassword='';

  constructor() { }

  ngOnInit() {

    this.signUpForm = new FormGroup({
      'username': new FormControl(null, [Validators.required, Validators.minLength(6), Validators.maxLength(15)]),
      'email': new FormControl(null, [Validators.required, Validators.email, Validators.minLength(5)]),
      'password': new FormControl(null, [Validators.required]),
      'confirmedPassword': new FormControl(null, [Validators.required, CustomValidators.passwordsMatch(this.password,this.confirmedPassword).bind(this)])
    });
  }

  onSubmit() {
    if (this.signUpForm.valid) {
      console.log(this.signUpForm.value);
    }
  }

}

custom-validators.ts

    import { FormControl } from '@angular/forms';

    export class CustomValidators{

    public static passwordsMatch(password: string, confirmedPassword: string) {

     return (control: FormControl) : { [s: string]: boolean } =>{
       //getting undefined values for both variables
       console.log(password,confirmedPassword);
        //if I change this condition to === it throws the error if the 
//  two fields are the same, so this part works
        if (password !== confirmedPassword) {
          return { 'passwordMismatch': true }
        } else {
          //it always gets here no matter what
          return null;
        }
    }
      }


    }
18
Juan

importer {AbstractControl, FormBuilder, FormGroup, Validators} de

définissez votre mot de passe dans le groupe et inutile d'utiliser "ngModel".

<div class="form-group row" formGroupName="passwords">
 <div class="form-group">
     <label for="password" class="control-label">Contraseña:</label>
        <input type="password" class="form-control" formControlName="password" title="Please enter your password">
              <p class="help-block" *ngIf="signUpForm.get('password').hasError('required') && signUpForm.get('password').touched">Debe ingresar una contraseña</p>
           </div>
          <div class="form-group">
            <label for="confirmedPassword" class="control-label">Confirmar Contraseña:</label>
            <input type="password" class="form-control" formControlName="confirmedPassword"  title="Please re-enter your password">
            <p class="help-block" *ngIf="signUpForm.get('confirmedPassword').hasError('required') && signUpForm.get('confirmedPassword').touched">La confirmación de contraseña no puede estar vacía</p>
            <p class="help-block" *ngIf="signUpForm.get('confirmedPassword').hasError('passwordMismatch') && signUpForm.get('confirmedPassword').touched">Las contraseñas no coinciden</p>
  </div>

     buildForm(): void {
            this.userForm = this.formBuilder.group({
                passwords: this.formBuilder.group({
                    password: ['', [Validators.required]],
                    confirm_password: ['', [Validators.required]],
                }, {validator: this.passwordConfirming}),

            });
        }

ajouter cette fonction personnalisée pour valider le mot de passe et confirmer le mot de passe

  passwordConfirming(c: AbstractControl): { invalid: boolean } {
    if (c.get('password').value !== c.get('confirm_password').value) {
        return {invalid: true};
    }
}

Erreur d'affichage lorsque le mot de passe ne correspond pas

<div style='color:#ff7355' *ngIf="userForm.get(['passwords','password']).value != userForm.get(['passwords','confirm_password']).value && userForm.get(['passwords','confirm_password']).value != null">
  Password does not match</div>
31
Shailesh Ladumor

Le problème est que vous êtes mélange du module de formulaires réactifs avec l'approche de saisie}. Cela vous amène à obtenir undefined lorsque vous transmettez les valeurs au validateur.

Vous n'avez pas besoin de vous lier au ng-model lorsque vous utilisez les formulaires réactifs. Au lieu de cela, vous devez accéder à la valeur des champs à partir de l'instance de FormGroup.

Je fais quelque chose comme ça dans une application pour valider la correspondance des mots de passe.

public Credentials: FormGroup;

ngOnInit() {
    this.Credentials = new FormGroup({});
    this.Credentials.addControl('Password', new FormControl('', [Validators.required]));
    this.Credentials.addControl('Confirmation', new FormControl(
        '', [Validators.compose(
            [Validators.required, this.validateAreEqual.bind(this)]
        )]
    ));
}

private validateAreEqual(fieldControl: FormControl) {
    return fieldControl.value === this.Credentials.get("Password").value ? null : {
        NotEqual: true
    };
}

Notez que le validateur attend un champ FormControl en tant que paramètre et compare la valeur du champ à celle de la valeur du champ Password de la CredentialsFormGroup.

Dans la HTML, veillez à supprimer le ng-model.

<input type="password" class="form-control" formControlName="confirmedPassword"  title="Please re-enter your password" >
<!-- AND -->
<input type="password" class="form-control" formControlName="password" title="Please enter your password">

J'espère que cela t'aides!

6
Daniel Ormeño

Veuillez mettre à jour le code de FormGroup comme ci-dessous dans Angular5 

 this.signUpForm = new FormGroup({
      'username': new FormControl(null, [Validators.required, Validators.minLength(6), Validators.maxLength(15)]),
      'email': new FormControl(null, [Validators.required, Validators.email, Validators.minLength(5)]),
      'password': new FormControl(null, [Validators.required]),
      'confirmedPassword': new FormControl(null, [Validators.required])
    }, this.pwdMatchValidator);

Ajouter la fonction pwdMatchValidator dans votre composant 

pwdMatchValidator(frm: FormGroup) {
    return frm.get('password').value === frm.get('confirmedPassword').value
       ? null : {'mismatch': true};
 }

Ajouter un message de validation dans votre modèle 

<span *ngIf="confirmedPassword.errors || signUpForm .errors?.mismatch">
              Password doesn't match
            </span>

Veuillez trouver le composant de travail de matériau angulaire ci-dessous. 

Composant Templete Code password.component.html

 <form class="cahnge-pwd-form" (ngSubmit)="onSubmit()" name="passwordForm" [formGroup]="passwordForm" #formDir="ngForm">
      <div fxLayout='column'>
    <mat-form-field>
      <input matInput name="password" placeholder="Password" [type]="hide ? 'text' : 'password'" formControlName="password" required>
      <mat-icon matSuffix (click)="hide = !hide">{{hide ? 'visibility_off' : 'visibility'}}</mat-icon>
      <mat-error *ngIf="password.invalid && (password.dirty || password.touched || isSubmit)">
        <span *ngIf="password.errors.required">
          Please enter a Password.
        </span>
        <span *ngIf="password.errors.maxlength">
          Please enter a Email no more than 16 characters.
        </span>
        <span *ngIf="password.errors.minlength">
          Please enter a password at least 6 characters.
        </span>
      </mat-error>
    </mat-form-field>
    <mat-form-field>
      <input matInput name="password" placeholder="Confirm Password" [type]="confirm_hide ? 'text' : 'password'" formControlName="confirm_password"
        required>
      <mat-icon matSuffix (click)="confirm_hide = !confirm_hide">{{confirm_hide ? 'visibility_off' : 'visibility'}}</mat-icon>
      <mat-error *ngIf="(confirm_password.invalid && (confirm_password.dirty || confirm_password.touched || isSubmit) || passwordForm.errors?.mismatch)">
        <span *ngIf="confirm_password.errors || passwordForm.errors?.mismatch">
          Password doesn't match
        </span>
      </mat-error>
    </mat-form-field>
    <div fxLayout='row' fxLayoutGap="10px">
      <button type="submit" mat-raised-button color="primary">Submit</button>
      <button type="button" (click)="formDir.resetForm(passwordForm)" mat-raised-button color="warn">Cancel</button>
    </div>
  </div>
</form>

Code du composant: password.component.ts

import { Component, OnInit, AfterViewInit } from '@angular/core';
import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms';
import { ToastrService } from 'ngx-toastr';
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
import { PasswordService } from './password.service';
import { PasswordValidation  } from './confirm';

@Component({
  selector: 'app-password',
  templateUrl: './password.component.html',
  styleUrls: ['./password.component.css']
})
export class PasswordComponent implements OnInit {
  passwordForm: FormGroup;
  isSubmit: boolean;
  constructor(private router: Router, private passwordService: PasswordService, private toastrService: ToastrService, private route: ActivatedRoute) { }
  ngOnInit() {
    this.passwordForm = new FormGroup({
      'password': new FormControl('', [
        Validators.required,
        Validators.minLength(6),
        Validators.maxLength(16),
      ]),
      'confirm_password': new FormControl('', [
         Validators.required,
        Validators.minLength(6),
        Validators.maxLength(16),
      ]),
    }, this.pwdMatchValidator);

  }
 pwdMatchValidator(frm: FormGroup) {
    return frm.get('password').value === frm.get('confirm_password').value
       ? null : {'mismatch': true};
 }

  get password() { return this.passwordForm.get('password'); }
  get confirm_password() { return this.passwordForm.get('confirm_password'); }

  onSubmit(formvalue):boolean {
    this.isSubmit = true;
    if (this.passwordForm.invalid) {
      return false;
    } else {
      this.passwordService.updatePassword(this.passwordForm.value)
      .subscribe((res) => {
        if (res.status == 'success') {
          this.toastrService.success(res.msg);
          this.router.navigate(['/change-password']);
        }
      })
      return true;
    }

  }

}

Il existe deux types de validateurs: validateur de groupe de formulaires et validateur de FormControl. Pour vérifier la correspondance de deux mots de passe, vous devez ajouter un validateur FormGroup. Voici mon exemple:

Note: this.fb est le FormBuilder injecté

this.newAccountForm = this.fb.group(
  {
    newPassword: ['', [Validators.required, Validators.minLength(6)]],
    repeatNewPassword: ['', [Validators.required, Validators.minLength(6)]],
  }, 
  {validator: this.passwordMatchValidator}
);

passwordMatchValidator(frm: FormGroup) {
  return frm.controls['newPassword'].value === frm.controls['repeatNewPassword'].value ? null : {'mismatch': true};
}

et dans le modèle:

<div class="invalid-feedback" *ngIf="newAccountForm.errors?.mismatch && (newAccountForm.controls['repeatNewPassword'].dirty || newAccountForm.controls['repeatNewPassword'].touched)">
  Passwords don't match.
</div>

Le point clé consiste ici à ajouter le validateur FormGroup en tant que second paramètre à la méthode group. 

3
Yang Zhang

Lorsque vous créez le validateur, vous transmettez les valeurs password et confirmedPassword, mais les modifications apportées à ces valeurs ne seront pas reflétées dans le validateur.

Les deux options que je vois sont:

  1. définissez votre validateur sur la FormGroup et recherchez les valeurs des deux contrôles que vous souhaitez comparer; ou
  2. puisque vous vous liez déjà à this, utilisez this.password et this.confirmedPassword dans votre validateur.
0

Je referais la même chose que Shailesh Ladumor mais en ajoutant la ligne suivante avant de revenir à la fonction de validation:

c.get('confirm_password').setErrors({'noMatch': true});

Pour que la fonction de validation ressemble à ceci:

passwordConfirming(c: AbstractControl): { invalid: boolean } {
    if (c.get('password').value !== c.get('confirm_password').value) {
        c.get('confirm_password').setErrors({'noMatch': true});
        return {invalid: true};
    }
}

Cela définira non seulement le trou userForm en tant que groupe de formulaire non valide, mais également confirm_password en tant que contrôle de formulaire non valide.

Avec cela, vous pouvez appeler ultérieurement la fonction suivante dans votre modèle:

public getPasswordConfirmationErrorMessage() {
if (this.userForm.get('confirm_password').hasError('required')) {
  return 'You must retype your password';
} else if (this.userForm.get('confirm_password').hasError('noMatch')) {
  return 'Passwords do not match';
} else {
  return '';
}

}