9

我正在尝试formGroup根据特定条件在控件中添加和删除验证器。

当我通过formGroup.updateValueAndValidity()整个表单更新验证器时,它没有更新,就好像我专门为每个控件申请一样formGroup.get('formControl').updateValueAndValidity(),它正在工作,但我必须为每个控件编写,我希望这不是正确的方法。我究竟做错了什么?

if (data == 'x') {
    this.myForm.get('control2').setValue(null);
    this.myForm.get('control2').setValidators(Validators.nullValidator);
    this.myForm.get('control1').setValidators(Validators.required);
} else if (data == 'y') {
    this.myForm.get('control1').setValue(null);
    this.myForm.get('control1').setValidators(Validators.nullValidator);
    this.myForm.get('control2').setValidators(Validators.required);
}
this.myForm.get('control1').updateValueAndValidity();
this.myForm.get('control2').updateValueAndValidity();

这是有效的,但是,

this.myForm.updateValueAndValidity();

这是行不通的。

4

6 回答 6

17

updateValueAndValidity()是自下而上的,因此如果您在控件上调用此方法,它将仅检查此控件及其父控件的验证,但不检查其子控件。

有关更多详细信息,请参阅github 上的AbstractControl#updateValueAndValidity以了解其工作原理。

  updateValueAndValidity(opts: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
    this._setInitialStatus();
    this._updateValue();

    if (this.enabled) {
      this._cancelExistingSubscription();
      (this as{errors: ValidationErrors | null}).errors = this._runValidator();
      (this as{status: string}).status = this._calculateStatus();

      if (this.status === VALID || this.status === PENDING) {
        this._runAsyncValidator(opts.emitEvent);
      }
    }

    if (opts.emitEvent !== false) {
      (this.valueChanges as EventEmitter<any>).emit(this.value);
      (this.statusChanges as EventEmitter<string>).emit(this.status);
    }

    if (this._parent && !opts.onlySelf) {
      this._parent.updateValueAndValidity(opts);
    }
  }
于 2019-11-13T08:46:41.590 回答
6

上周我也偶然发现了这一点,我得出的结论是这是预期的行为。文档说明了以下内容:

默认情况下,它还会更新其祖先的值和有效性。

请注意,它说的是“祖先”而不是“后代”。这意味着当您运行updateValueAndValidity()andcontrol1并且control2它们都有效时,它们也myForm将被标记为有效。

于 2019-11-13T08:47:28.233 回答
4

正如 Serginho 在接受的答案中所说,问题在于 updateValueAndValidity 方法是自下而上的,因此它不会验证子控件。一个可能的解决方案是为 FormGroup 类编写一个自定义原型方法,如下所示:

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

declare module '@angular/forms/forms' {
  interface FormGroup {
    validate(): void;
  }
}

FormGroup.prototype.validate = function(this: FormGroup): void {
  for (const key in this.controls) {
    const formElement = this.get(key);
    if (formElement instanceof FormControl) {
      formElement.updateValueAndValidity();
    } else if (formElement instanceof FormGroup) {
      formElement.validate();
    }
  }
};

validate 是一种递归方法,它为树 (FormGroup) 的每个叶子 (FormControl) 调用 updateValueAndValidity,即使在嵌套表单组的情况下也是如此。

不要忘记在需要使用 validate 方法的地方导入模块:

import '../core/extensions/formGroup';
于 2020-06-22T13:57:57.147 回答
1

免责声明:这可能是不好的做法,但它做了 Angular 不允许我简单做的事情。请注意,与 Angular 处理验证的方式相比,这是非常低效的。以下是如何刷新整个表单验证:

  validateForm(control: AbstractControl) {
    control['controls'].forEach(
      // make sure to pass {onlySelf: true, emitEvent: false} 
      // or it will recurse indefinitely
      control => control.updateValueAndValidity({onlySelf: true, emitEvent: false})
    );

    return null;
  }

对于我的用例,我需要在每次用户更改任何字段时更新整个表单的有效性(而不仅仅是当前字段的验证或仅触及的字段,yadi yada):

this.form.setValidators(this.validateForm);
于 2020-10-07T08:18:16.623 回答
0

尝试这个:

const forms = [this.form1, this.form2, this.form3];

    for (let f of forms) {
      for (let key in f.controls) {
        f.controls[key].updateValueAndValidity();
      }
    }
于 2021-03-12T13:42:55.713 回答
0

我喜欢@ssnake 的解决方案,但它缺乏 FormArray 支持。对其进行了一些修改,也使其与类无关,只需检查是否有控件并为每个控件触发验证,无论是 FormControl、FormGroup 还是 FormArray:

declare module '@angular/forms/forms' {
  interface AbstractControl {
    validate(): void;
  }
}

AbstractControl.prototype.validate = function(): void {
  this.updateValueAndValidity();

  if ('controls' in this) {
    for (const key in this.controls) {
      if (this.controls.hasOwnProperty(key)) {
        this.get(key).validate();
      }
    }
  }
};
于 2022-02-03T16:06:48.103 回答