46

我正在尝试实现一个自定义验证器来检查密码和密码确认是否相等。问题是验证器正在获取未定义的密码和确认密码参数。我如何使这项工作。该函数有效,因为如果我将条件更改为 === 而不是 !== 它会在字段相同时正确抛出错误。有谁知道这里的错误是什么?

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="example@gmail.com">
            <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>

注册组件.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);
    }
  }

}

自定义验证器.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;
        }
    }
      }


    }
4

10 回答 10

55

导入 {AbstractControl, FormBuilder, FormGroup, Validators} 从

将您的密码输入到组中,无需使用“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">Password must be required</p>
     <p class="help-block" *ngIf="signUpForm.get('confirmedPassword').hasError('passwordMismatch') && signUpForm.get('confirmedPassword').touched">password does not match</p>
  </div>

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

            });
        }

添加此自定义功能以验证密码和确认密码

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

密码不匹配时显示错误

<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>
于 2017-06-09T05:18:28.230 回答
18

问题是您将反应式表单模块与输入方法混合在一起。这导致您undefined在将值传递给验证器时得到。

ng-model使用反应形式时,您不需要绑定到。相反,您应该从 Instance 访问字段的值FormGroup

我在应用程序中做这样的事情来验证密码是否匹配。

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

请注意,验证器需要一个FormControl字段作为参数,并将该字段的值PasswordCredentials FormGroup.

HTML确保删除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">

希望这可以帮助!

于 2017-06-09T05:16:48.017 回答
10

有两种类型的验证器:FormGroup 验证器FormControl 验证器。要验证两个密码是否匹配,您必须添加一个 FormGroup 验证器。下面是我的例子:

注意:this.fb 是注入的 FormBuilder

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

在寺庙里:

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

这里的重点是将 FormGroup 验证器作为第二个参数添加到 group 方法中。

于 2018-04-03T10:35:16.967 回答
8

请在 Angular5 中更新 FormGroup 代码,如下所示

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

pwdMatchValidator在组件中添加功能

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

在模板中添加验证消息

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

请找到下面的角材料工作组件。

组件模板代码 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>

组件代码: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;
    }

  }

}
于 2018-03-11T11:14:56.430 回答
4

我会做与Shailesh Ladumor相同的操作,但在返回验证函数之前添加以下行:

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

因此验证函数如下所示:

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

这不仅会将孔设置userForm为无效的表单组,还会将其设置confirm_password为无效的表单控件。

有了这个,您以后可以在模板中调用以下函数:

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

}

于 2018-03-29T09:30:49.403 回答
3

当您需要对多个字段进行验证,并且希望在表单创建时声明验证器时,必须使用FormGroup 验证器。表单验证器的主要问题是它将错误附加到表单而不是验证控件,这导致模板中的一些不一致。这是可重用的表单验证器,它将错误附加到表单和控件

// in validators.ts file

export function controlsEqual(
  controlName: string,
  equalToName: string,
  errorKey: string = controlName // here you can customize validation error key 
) {

  return (form: FormGroup) => {
    const control = form.get(controlName);

    if (control.value !== form.get(equalToName).value) {
      control.setErrors({ [errorKey]: true });
      return {
        [errorKey]: true
      }
    } else {
      control.setErrors(null);
      return null
    }
  }
}

// then you can use it like
  ngOnInit() {
    this.vmForm = this.fb.group({
      username: ['', [Validators.required, Validators.email]],
      password: ['', [
        Validators.required,
        Validators.pattern('[\\w\\d]+'),
        Validators.minLength(8)]],
      confirm: [''], // no need for any validators here
    }, { 
        // here we attach our form validator
        validators: controlsEqual('confirm', 'password')
      });
  }

于 2019-06-21T10:09:16.443 回答
2
this.myForm = this.fb.group({
    userEmail: [null, [Validators.required, Validators.email]],
    pwd: [null, [Validators.required, Validators.minLength(8)]],
    pwdConfirm: [null, [Validators.required]],
}, {validator: this.pwdConfirming('pwd', 'pwdConfirm')});



pwdConfirming(key: string, confirmationKey: string) {
    return (group: FormGroup) => {
        const input = group.controls[key];
        const confirmationInput = group.controls[confirmationKey];
        return confirmationInput.setErrors(
            input.value !== confirmationInput.value ? {notEquivalent: true} : null
        );
    };
}
于 2019-12-30T08:34:07.500 回答
1

只想补充。我使用 Reactive 表单,因此很容易在表单上使用订阅 valueChanges 进行自定义验证

this.registrationForm.valueChanges.subscribe(frm => {
  const password = frm.password;
  const confirm = frm.confirmPassword;
  if (password !== confirm) {
    this.registrationForm.get('confirmPassword').setErrors({ notMatched: true });
  }
  else {
    this.registrationForm.get('confirmPassword').setErrors(null);
  }
});

}

于 2021-08-08T05:46:31.567 回答
0

当您创建验证器时,您将传递 和 的值passwordconfirmedPassword但这些值的更改不会反映在验证器中。

我看到的两个选项是:

  1. 在 上定义您的验证器FormGroup,并从您要比较的两个控件中查找值;或者
  2. 因为你已经绑定了,所以在你的验证器中this使用this.password和。this.confirmedPassword
于 2017-06-09T05:17:38.367 回答
0

只是为了多样性,我添加了另一种方法来做到这一点。基本上,我创建了一个简单的自定义验证器,它采用原始控件(第一个输入字段)并使用重新输入控件(第二个控件)检查它的值。

import { AbstractControl, ValidatorFn } from "@angular/forms";

export abstract class ReEnterValidation {
   static reEnterValidation(originalControl: AbstractControl): ValidatorFn {
       return (control: AbstractControl): { [s: string]: boolean } => {
           if (control.value != originalControl.value) {
               return { 'reentry': true };
           }
       };
   }
}

您可以在创建新表单控件时设置此验证器:

control1 = new FormControl('', ReEnterValidation.reEnterValidation(control2));

或者,您可以在之后设置它:

control.setValidators(ReEnterValidation.reEnterValidation(this._newPinControl));
control.updateValueAndValidity();

在视图中,您可以显示以下错误:

<!-- Confirm pin validation -->
<div *ngIf="control2.invalid && (control2.dirty || control2.touched)">
    <div *ngIf="control2.errors.reentry">
        The re-entered password doesn't match. Please try again.
    </div>
</div>
于 2020-04-25T10:56:04.743 回答