Angular 响应式表单中的自定义验证器无法正常工作

Custom Validator in Angular Reactive Forms not working

我在字段上有一个自定义验证器 postalCode:

function postalCodeValidator(): ValidatorFn {
  return (control: AbstractControl): ValidationErrors | null => {
    if (!this.contactForm) {
      return null;
    }

    const isPotalCodeRequired = this.contactForm.get(
      'isPotalCodeRequired'
    ).value;

    if (isPotalCodeRequired && !control.value) {
      console.log('here');
      this.contactForm.controls['postalCode'].setErrors({ required: true });
    } else {
      this.contactForm.controls['postalCode'].setErrors({ required: false });
    }

    return null;
  };
}

检查另一个字段 isPotalCodeRequired 以查看是否应将验证应用于 postalCode 字段。

如果 isPotalCodeRequired 为真,则 postalCode 需要一个值,否则可以留空。但是当我在 postalCode 字段上调用 ​​setErrors 时,我的自定义验证似乎没有按预期工作。它在自定义验证器函数中添加它,但在函数执行后检查它,错误不再出现在 postalCode 字段中。

Demo.

您尝试做的是所谓的 cross-field 验证,通常使用 cross-field 验证器,您应该将验证器应用到 FormGroup 而不是 FormControl .这里,

this.contactForm = this.formBuilder.group({
  isPotalCodeRequired: [true],
  postalCode: [null, Validators.compose([postalCodeValidator.call(this)])],
});

您正在将 postalCodeValidator 绑定到特定控件。当您应用这样的验证器时,该验证器应该 return 您想要应用到控件的验证消息,但您 returning null,

  if (isPotalCodeRequired && !control.value) {
    console.log('here');
    this.contactForm.controls['postalCode'].setErrors({ required: true });
  } else {
    this.contactForm.controls['postalCode'].setErrors({ required: false });
  }

  return null;

这会清除应用于该控件的所有验证消息。相反,将此验证器绑定到包含您的两个控件的 FormGroup

this.contactForm = this.formBuilder.group({
  isPotalCodeRequired: [true],
  postalCode: [null]
}, {validators: Validators.compose([postalCodeValidator.call(this)])});

现在传递给验证器的 AbstractControl 就是您的整个 FormGroup。这样做的好处是可以访问您需要的所有控件。因此,您将不会引用 this.contactForm,而是

control.controls['postalCode'].setErrors({ required: true });

您应该能够从验证器中删除对 this.contactForm 的所有引用。更重要的是,您的 null return 将不再清除单个控件上的验证消息。查看 Angular docs 上的 cross-field 反应式表单验证。

但是有一种更简洁的方法可以完全做到这一点。无需编写自定义验证器,您只需监听 isPostalCodeRequired 和 add/remove 所需的 built-in 所需验证器的更改,

this.contactForm.get('isPostalCodeRequired').valueChanges.subscribe(val => {
  if (val) { 
    //Add validator
  else {
    //Remove validator
  })

add/remove 验证器可用的辅助函数取决于您的 Angular 版本,但它们非常简单。

编辑:更新了解决演示中代码的答案。

Angular 的验证函数有点奇怪。当控件没有错误时,您需要 return null 和包含错误的对象以及错误时的简短描述:

function postalCodeValidator(): ValidatorFn {
  return (control: AbstractControl): ValidationErrors | null => {
    if (!this.contactForm) {
      return null;
    }

    const isPotalCodeRequired = this.contactForm.get(
      'isPotalCodeRequired'
    ).value;

    if (isPotalCodeRequired && !control.value) {
      console.log('here');
      return {required: 'Postal code is required' };
    }

    return null;
  };
}

您无需手动设置错误,因为框架会为您完成