Angular 6,this.formGroup.updateValueAndValidity()无法正常工作

19

我正在尝试根据某些条件向 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();

这个不起作用。


2
我相信不幸的是,你将不得不分别针对每个控制器。此外,如果需要多次重复使用,您可以编写一个方法,在每次调用它时自动执行此操作。 - mikegross
不如调用 this.myForm.markAllAsTouched(); - Ε Г И І И О
7个回答

30

updateValueAndValidity()是自下而上的,所以如果你在一个控件上调用此方法,它只会检查该控件及其父级的验证,但不会检查其子控件。

更多详情请参见AbstractControl#updateValueAndValidity在GitHub上的工作原理。

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

22
这到底有什么意义?所以如果我想要在整个FormGroup上触发一次检查,我只能在一个控件上调用它吗?他们当时在想什么啊? - AsGoodAsItGets
1
@AsGoodAsItGets 我认为你甚至需要在每个子元素上调用它。我同意这很愚蠢。 - Louisb Boucquet

9

上周我也遇到了这个问题,并得出结论,这是预期行为。 文档中指出:

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

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


8

正如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';

2
免责声明: 这可能是不好的实践,但它可以做到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;
  }

对于我的使用情况,我需要在用户更改任何字段时更新整个表单的有效性(而不仅仅是当前字段的验证或仅触摸过的字段,等等):

this.form.setValidators(this.validateForm);

1

我喜欢 @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();
      }
    }
  }
};

0

试试这个:

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

    for (let f of forms) {
      for (let key in f.controls) {
        f.controls[key].updateValueAndValidity();
      }
    }

0
使用以下方法验证任何FormControlFormGroupFormArray
UpdateValueAndValidity<T extends AbstractControl>(control: T): void {
  try {
    if (control instanceof FormGroup) {
      control.updateValueAndValidity()

      const controls = control.controls
      Object.keys(controls).forEach(key => {
        this.UpdateValueAndValidity(controls[key])
      })
    }
    else if (control instanceof FormArray) {
      control.updateValueAndValidity()

      control.controls.forEach(formControl => {
        this.UpdateValueAndValidity(formControl)
      })
    }
    else if (control instanceof FormControl) {
      control.updateValueAndValidity()
    }
    else {
      throw new Error('Error: unexpected control value')
    }
  } catch (error) {
    console.log(error)
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接