Angular6、this.formGroup.updateValueAndValidity()不能正常工作
Angular 6, this.formGroup.updateValueAndValidity() not working properly
我正在尝试根据特定条件在 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();
这不起作用。
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);
}
}
我上周也偶然发现了这个问题,我得出的结论是这是预期的行为。 docs 声明如下:
By default, it also updates the value and validity of its ancestors.
请注意,它说的是 "ancestor" 而不是 "descendants"。这意味着当您在 control1
和 control2
上有 运行 updateValueAndValidity()
并且它们都有效时,myForm
也将被标记为有效。
正如 Serginho 在接受的答案中所说,问题在于 updateValueAndValidity 方法是自下而上的,因此它不会验证子控件。
一个可能的解决方案是为 FormGroup class 编写自定义原型方法,如下所示:
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,即使在嵌套表单组的情况下也是如此。
不要忘记在需要使用验证方法的地方导入模块:
import '../core/extensions/formGroup';
免责声明:这可能是不好的做法,但它做了 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);
试试这个:
const forms = [this.form1, this.form2, this.form3];
for (let f of forms) {
for (let key in f.controls) {
f.controls[key].updateValueAndValidity();
}
}
我喜欢@ssnake 的解决方案,但它缺少 FormArray 支持。稍微修改了一下,也做了class-agnostic,只是检查是否有控件,并为每个控件触发验证,无论是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();
}
}
}
};
我正在尝试根据特定条件在 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();
这不起作用。
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);
}
}
我上周也偶然发现了这个问题,我得出的结论是这是预期的行为。 docs 声明如下:
By default, it also updates the value and validity of its ancestors.
请注意,它说的是 "ancestor" 而不是 "descendants"。这意味着当您在 control1
和 control2
上有 运行 updateValueAndValidity()
并且它们都有效时,myForm
也将被标记为有效。
正如 Serginho 在接受的答案中所说,问题在于 updateValueAndValidity 方法是自下而上的,因此它不会验证子控件。 一个可能的解决方案是为 FormGroup class 编写自定义原型方法,如下所示:
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,即使在嵌套表单组的情况下也是如此。
不要忘记在需要使用验证方法的地方导入模块:
import '../core/extensions/formGroup';
免责声明:这可能是不好的做法,但它做了 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);
试试这个:
const forms = [this.form1, this.form2, this.form3];
for (let f of forms) {
for (let key in f.controls) {
f.controls[key].updateValueAndValidity();
}
}
我喜欢@ssnake 的解决方案,但它缺少 FormArray 支持。稍微修改了一下,也做了class-agnostic,只是检查是否有控件,并为每个控件触发验证,无论是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();
}
}
}
};