如何触发响应式表单中输入的验证?

How can I trigger validations for inputs in ReactiveForms?

我在 Reactive Forms 中开发了一个表单,用户可以在其中设置新密码。他输入旧密码和新密码并确认新密码。 我考虑过以下情况:

开发工作也很好。现在我有一件事不太好。仅当我更改输入字段中的某些内容时才会触发验证。但是现在想确认密码的时候要修改新密码,这样才知道有没有错误。我读到这可以通过函数 updateValueAndValidity 来完成。你知道怎么做吗?

我的代码:

// TS
changePasswordForm: FormGroup;
submitted = false;

ngOnInit() {
    // To initialize forms
    this.initChangePasswordForm();
  }

  // Creation of the changePasswordForm
  private initChangePasswordForm() {
    // General
    this.changePasswordForm = this.formBuilder.group({
      old_password: [null, Validators.pattern('^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[^\w\s]).{8,}$')],
      new_password: [null, this.customComparePasswordValidator],
      confirm_password: [null, Validators.required]
    });
  }

 customComparePasswordValidator(control: FormControl) {
    const newPassword = control.value;
    if (newPassword && newPassword.length) {
      const oldPassword = control.parent.value.old_password;
      if (oldPassword && oldPassword.length && newPassword.toLowerCase().includes(oldPassword.toLowerCase())) {
        return { newPasswordIncludesOldPassword: true };
      }
      const pattern = new RegExp('^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[^\w\s]).{8,}$');
      if (!pattern.test(newPassword)) {
        return { newPasswordInvalid: true };
      }
    } else {
      return { required: true };
    }
  }
// HTML
<input matInput type="password" placeholder="aktuelles Passwort" formControlName="old_password" required>
 <p *ngIf="changePasswordForm.get('old_password').invalid && (changePasswordForm.get('old_password').dirty || changePasswordForm.get('old_password').touched)">
            <mat-error class="custom-validation-error" *ngIf="changePasswordForm.get('old_password').hasError('required')">aktuelles Passwort eingeben</mat-error>
            <mat-error class="custom-validation-error" *ngIf="changePasswordForm.get('old_password').hasError('pattern')">8 oder mehr Zeichen mit einer Mischung aus Groß- und Kleinbuchstaben, Ziffern und Symbolen verwenden</mat-error>
          </p>

<input matInput type="password" placeholder="neues Passwort" formControlName="new_password" required>
<p *ngIf="changePasswordForm.get('new_password').invalid && (changePasswordForm.get('new_password').dirty || changePasswordForm.get('new_password').touched)">
            <mat-error class="custom-validation-error" *ngIf="changePasswordForm.get('new_password').hasError('required')">neues Passwort eingeben</mat-error>
            <mat-error class="custom-validation-error" *ngIf="changePasswordForm.get('new_password').hasError('newPasswordInvalid')">8 oder mehr Zeichen mit einer Mischung aus Groß- und Kleinbuchstaben, Ziffern und Symbolen verwenden</mat-error>
            <mat-error class="custom-validation-error" *ngIf="changePasswordForm.get('new_password').hasError('newPasswordIncludesOldPassword')">Neues und altes Passwort dürfen nicht gleich sein</mat-error>
          </p>

 <input matInput type="password" placeholder="Passwort bestätigen" formControlName="confirm_password" appConfirmEqualValidator="new_password" required>
 <p *ngIf="changePasswordForm.get('confirm_password').invalid && (changePasswordForm.get('confirm_password').dirty || changePasswordForm.get('confirm_password').touched)">
            <mat-error class="custom-validation-error" *ngIf="changePasswordForm.get('confirm_password').hasError('required')">Passwort erneut eingeben</mat-error>
            <mat-error class="custom-validation-error" *ngIf="changePasswordForm.get('confirm_password').hasError('notEqual') && !changePasswordForm.get('confirm_password').hasError('required')">Passwort stimmt nicht überein</mat-error>
          </p>

我的 StackBlitz:https://stackblitz.com/edit/example-angular-material-reactive-form-sr1keu?file=app%2Fapp.component.html

正如您所说,验证器仅在您更改 FormControl 时“检查”。因此,您可以在更改第二个控件时“强制”使一个控件生效。所以,例如你可以简单地做,在 FormControl "new_password" 中写

<input matInput ... formControlName="new_password"
     (input)="changePasswordForm.get('confirm_password').updateValueAndValidity()"  />

(与其他 FormControl 相同)

您也可以在创建表单后订阅 valueChanges -别忘了取消订阅-

  private initChangePasswordForm() {
    // After create the formGroup
    this.changePasswordForm = this.formBuilder.group({..}

    //subscribe to changes "new_password"
    this.changePasswordForm.get('new_password').valueChanges
       .subscribe(_=>{
            this.changePasswordForm.get('confirm_password').updateValueAndValidity()     
    })

另一种方法是在整个表单上创建一个 customValidator。当你使用 material 时,你需要使用 setErrors。验证器可以像

  validatorRepeatPassword(form: FormGroup) {
    const errorOldPassword = {};
    const errorNewPassword = {};
    const errorConfirmPassword = {};
    const pattern = new RegExp(
      '^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[^\w\s]).{8,}$'
    );
    if (!form.value.old_password) errorOldPassword['required'] = true;
    else {
      if (!pattern.test(form.value.old_password))
        errorOldPassword['pattern'] =
          '8 or more characters with a mixture of upper and lower case letters,use numbers and symbols';
    }
    if (!form.value.new_password) errorNewPassword['required'] = true;
    else {
      if (!pattern.test(form.value.new_password))
        errorNewPassword['pattern'] =
          '8 or more characters with a mixture of upper and lower case letters,use numbers and symbols';
      if (
        form.value.old_password &&
        form.value.new_password
          .toLowerCase()
          .includes(form.value.old_password.toLowerCase())
      )
        errorNewPassword['newPasswordIncludesOldPassword'] = true;
    }
    if (!form.value.confirm_password) errorConfirmPassword['required'] = true;
    else {
      if (
        form.value.new_password &&
        form.value.confirm_password != form.value.new_password
      )
        errorConfirmPassword['notEqual'] = true;
    }
    form.controls.old_password.setErrors(
      Object.keys(errorOldPassword).length ? errorOldPassword : null
    );
    form.controls.new_password.setErrors(
      Object.keys(errorNewPassword).length ? errorNewPassword : null
    );
    form.controls.confirm_password.setErrors(
      Object.keys(errorConfirmPassword).length ? errorConfirmPassword : null
    );
    return {
      ...errorOldPassword,
      ...errorNewPassword,
      ...errorConfirmPassword,
    };
  }

并使用

this.changePasswordForm = this.formBuilder.group(
  {
    old_password: null,
    new_password: null,
    confirm_password: null,
  },
  { validator: this.validatorRepeatPassword }
);

最后一个方法(我只用new_password制作,确认密码是用两个控件制作验证器。像

这样的函数
  matchValidator(field: string,not:boolean=false) {
    return (control: AbstractControl) => {
      const parent = control.parent;
      const controlCompare = parent ? parent.get(field) : null;
      if (controlCompare && controlCompare.value && control.value) {
        const invalid = controlCompare.value != control.value;
        if (invalid!=controlCompare.invalid)
        {
          setTimeout(()=>{
            controlCompare.updateValueAndValidity();

          })
        }

        return invalid && !not ? { match: 'no match' } : null;
      }
    };
  }

允许我们写,例如

this.changePasswordForm = this.formBuilder.group(
  {
    old_password: null,
    new_password: [null,this.matchValidator('confirm_password',true)],
    confirm_password: [null,this.matchValidator('confirm_password',true)]
  }
);

看到你用一个函数来传递参数-应该是固定的-