Angular表单更新所有子控件的updateValueAndValidity方法

48
在我的Angular4应用程序中,我有一个带有多个控件的表单。 有时我需要强制更新它们的有效性,所以我这样做:

this.form.get('control1').updateValueAndValidity();
this.form.get('control2').updateValueAndValidity();
this.form.get('control3').updateValueAndValidity();
// and so on....

然后:

this.form.updateValueAndValidity();

这个运行良好。

然而,我在想是否有更好的方法来完成相同的事情,只需要在父表单上调用一个方法即可。

根据其文档updateValueAndValidity()方法:

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

但在我的情况下,我需要更新其后代的值和有效性。因此,我可以摆脱很多代码。


你试过一些东西来看它是否更新其子项了吗? - jonrsharpe
something like ... ? - Francesco Borzi
不先直接调用子类的方法吗?我本来以为 updateValueAndValidity 也会应用于子表单。 - jonrsharpe
当然可以,但它不起作用。 - Francesco Borzi
1
有趣的问题。我认为它不是开箱即用的,这种情况下也存在与 markAsDirty 相同的问题:https://github.com/angular/angular/issues/11774 我认为你需要做一些变通,并迭代父对象属性并在每个控件上使用 updateValueAndValidity。嗯。虽然很方便,但只需在父级上调用它就可以了。 - AT82
请参考以下相关问题,以获取一些选项:https://dev59.com/bVgQ5IYBdhLWcg3wym0Y - mc01
6个回答

20

我遇到了更新嵌套层级控件的FormGroup | FormArray的相同情况。

看看这个(对我有效):

/**
 * Re-calculates the value and validation status of the entire controls tree.
 */
function updateTreeValidity(group: FormGroup | FormArray): void {
  Object.keys(group.controls).forEach((key: string) => {
    const abstractControl = group.controls[key];

    if (abstractControl instanceof FormGroup || abstractControl instanceof FormArray) {
      updateTreeValidity(abstractControl);
    } else {
      abstractControl.updateValueAndValidity();
    }
  });
}

1
元素隐式具有“任意”类型,因为类型为“字符串”的表达式不能用于索引类型“{ [key: string]: AbstractControl<any, any>; } | AbstractControl<any, any>[]”。 在类型“{ [key: string]: AbstractControl<any, any>; } | AbstractControl<any, any>[]”上找不到带有类型“字符串”的参数的索引签名。ts(7053) - farahm

15

10

我解决了一个与你的问题类似的问题,通过递归控件并手动触发更新。可能这不是最优的解决方案:

private triggerValidation(control: AbstractControl) {
    if (control instanceof FormGroup) {
        const group = (control as FormGroup);

        for (const field in group.controls) {
            const c = group.controls[field];

            this.triggerValidation(c);
        }
    }
    else if (control instanceof FormArray) {
        const group = (control as FormArray);

        for (const field in group.controls) {
            const c = group.controls[field];

            this.triggerValidation(c);
        }
    }

    control.updateValueAndValidity({ onlySelf: false });
}

2

如果您需要触发控件及其子级的验证,有一种解决方法是在不属于公共API的情况下调用updateTreeValidity

(<any>form)._updateTreeValidity({ emitEvent: false });

这个问题正在这里跟踪:https://github.com/angular/angular/issues/6170

2
validateFormFields(fields) {
    try {
      Object.entries(fields.controls).forEach((control: any) => {
        if (typeof control[0] == 'Array' 
        ) {
          this.validateFormFields(control[1]);
        } else {
          if (control[1].controls) {
            Object.entries(control[1].controls).forEach((c: any) => {
              c[1].touched = true;
            });
          } else {
            control[1].touched = true;
          }

        }
      });
    } catch (e) {
      console.log(e);
    }
  }

将控件标记为已触摸是其他答案中缺少的部分! - Emmanuel

1
我遇到了同样的问题,其中一个问题是监听valueChanges可观察对象并触发updateValueAndValidity来运行我的验证器导致了无限循环。为了解决这个问题(有点hacky?),我创建了组件变量validityCycleFired并将其初始化为false,然后切换它以防止无限循环:
const fireValidityCheck = () => {
     if (this.validityCycleFired) return;
     this.validityCycleFired = true;
     this.passwordForm.get('old_password').updateValueAndValidity();
     this.passwordForm.get('new_password').updateValueAndValidity();
     this.passwordForm.get('confirm_new_password').updateValueAndValidity();
     setTimeout(() => this.validityCycleFired = false, 15);
     return;
};
this.passwordForm.valueChanges.subscribe(fireValidityCheck);

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