【问题标题】:How to retrigger Validator when a different field changes value当不同的字段更改值时如何重新触发验证器
【发布时间】:2021-07-25 17:14:54
【问题描述】:

我编写了一个简单的自定义验证器来比较两个密码字段并在它们不匹配时发出错误信号。这里是:

@Directive({
  selector: '[appMatchPassword]',
  providers: [
    { provide: NG_VALIDATORS, useExisting: MatchPasswordDirective, multi: true }
  ]
})
export class MatchPasswordDirective implements Validator {

  @Input('appMatchPassword') otherControl: string = '';

  constructor() { }

  registerOnValidatorChange(fn: () => void): void {
  }

  validate(control: AbstractControl): ValidationErrors | null {

    const compareTo = control.parent?.get(this.otherControl);
    if (!compareTo) return null;

    if (compareTo?.value !== control.value) return {
      appMatchPassword: true
    }

    return null;
  }

}

模板这样引用它:

    <mat-form-field>
      <mat-label>Password</mat-label>
      <input matInput type="password" name="password" [(ngModel)]="user.password"
            required minlength="4" #iPassword="ngModel">
      <mat-error *ngIf="iPassword.errors?.required">Password Required.</mat-error>
      <mat-error *ngIf="iPassword.errors?.minlength">Minimum 4 characters</mat-error>
    </mat-form-field>

    <mat-form-field>
      <mat-label>Verify Password</mat-label>
      <input matInput type="password" name="vpassword" [(ngModel)]="vpassword"
            required appMatchPassword="password" #fvp="ngModel">
      <mat-error *ngIf="fvp.errors?.appMatchPassword">Passwords do not match</mat-error>
    </mat-form-field>

如果用户在password 字段中输入一个值,在vpassword 字段中输入一个不同的值,这将按需要工作。如果用户更正了vpassword 字段中的值,错误就会显示出来并消失。

但是,如果用户返回并编辑 password 字段,错误不会消失。验证器仅在其自己的 AbstractControl 更改值后才被调用。

password 字段发生变化时,有什么好的方法可以触发验证器吗?

【问题讨论】:

  • 这能回答你的问题吗? Confirm password validation in Angular 6
  • @Yasser 是的。如果我第一次看到,我就不会发布这个问题。但是,我认为 Aviad 下面的答案更新颖、更简洁,因此我将其标记为答案,希望它能对下一个人有所帮助。

标签: angular


【解决方案1】:

您必须使用 cross-field validation 并将您的验证器指令应用于表单本身,而不是密码字段。

如果验证器发出错误,它将出现在表单的错误数组中,而不是特定字段的数组中。

此外,如果您想在mat-form-field 中显示mat-error,它将不会显示,因为该字段本身并非无效(仅整个表单)-因此您可以将mat-error 移到外面,或使用custom error state matcher

简而言之,这是您的指令的外观(请注意,现在传递给 validate 方法的控件是整个表单,而不是特定字段)

validate(control: AbstractControl): ValidationErrors | null {
  const pass = control.get('password')?.value;
  const vpass = control.get('vpassword')?.value;

  console.log(pass,vpass);

  if (pass !== vpass)
    return {
      appMatchPassword: true
    };

  return null;
}

你的模板应该是这样的:

<!-- the directive is applied to the form -->
<form #frm="ngForm" appMatchPassword>
  <mat-form-field>
    <mat-label>Password</mat-label>
    <input matInput type="password" name="password" [(ngModel)]="user.password"
      required minlength="4" #iPassword="ngModel">
    <mat-error *ngIf="iPassword.errors?.required">Password Required.</mat-error>
    <mat-error *ngIf="iPassword.errors?.minlength">Minimum 4 characters</mat-error>
  </mat-form-field>

  <mat-form-field>
    <mat-label>Verify Password</mat-label>

    <!-- the directive is NOT applied to the particular field -->
    <!--   also note the `matcher` property, see below... -->
    <input matInput type="password" name="vpassword" [(ngModel)]="vpassword" required
           [errorStateMatcher]="matcher">

    <!-- this line changed, it now looks at frm.errors instead of fvp.errors -->
    <mat-error *ngIf="frm.errors?.appMatchPassword">Passwords do not match</mat-error>
  </mat-form-field>
</form>

这是一个虚拟错误状态匹配器,它总是显示字段中存在的任何mat-error

export class AlwaysShowErrors implements ErrorStateMatcher {
  isErrorState(control: FormControl, form: FormGroupDirective | NgForm): boolean {
    return true;
  }
}

在组件类中将其实例化为matcher 字段:

matcher = new AlwaysShowErrors();

StackBlitz 示例

【讨论】:

    猜你喜欢
    • 2013-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-06
    • 2021-12-14
    相关资源
    最近更新 更多