具有formgroup的子ControlValueAccessor组件 - 表达式在检查后已更改

时间:2018-02-22 09:18:29

标签: angular

加载我的组件时,我正在使用patchValue更新该(父)组件的表单。

on the form form我的孩子ControlValueAccessor组件也有一个formgroup。因此,在我的writeValue函数上,我正在更新子组件有关从父组件patchValue传递的值。

我的writeValue函数看起来像这样:

    writeValue(value: CountryAndCity): void {
    if (value != null && value.country && value.city) {
      this.isDefaultValueDefined = true;
      this.form.patchValue({country: value.country});
      this.form.patchValue({city: value.city});
      **this.cdRef.detectChanges();**
    }
  }

正如你所看到的,我不得不使用detectChanges()作为上述函数的最后一行,因为没有它我得到错误:

ParentComponent.html:1错误错误:ExpressionChangedAfterItHasBeenCheckedError:检查后表达式已更改。以前的价值:' ng-pristine:true'。当前价值:' ng-pristine:false'。

我想我明白为什么会这样。 Angular开始检查父组件,然后,在子组件上检查子组件,然后在子组件上,在writeValue函数上更改父组件的表单状态。

我对使用detectChanges()感到不舒服。有没有办法做别的事情?是否必须使用父组件和子组件,每个组件都有formGroup,子组件是父formgroup formControl之一?

3 个答案:

答案 0 :(得分:1)

我不认为这是由于您的write函数而发生的,而是您的表单中的某些内容在孩子创建之后就正在发出值,但是很难不看更多您的代码。

我遇到了同样的问题,因此在不手动触发更改检测的情况下防止此错误的方法是将值传播到当前更改检测周期的父级解耦。

一种简单的方法是将yield设为上游值,例如

this.form
  .valueChanges
  .pipe(
    delay(0)
  )
  .subscribe(it => {
    this.propagateChange(...)
  })

我不知道这是否是最好的方法,但对我而言,它可行。

顺便说一句:我还建议使用{emitEvent: false}作为补丁选项,以防止孩子在write函数中触发事件。如果您对错误根源的假设是正确的,那么这将解决您的问题,但是如果我的假设是正确的,那么就不会。

答案 1 :(得分:0)

您的方法很好,@ Tobi的答案也很好!

也就是说,如果您不想在构建嵌套表单(使用子组件)/创建自定义控件值访问器方面花费太多精力,则绝对应该签出以下库:https://github.com/cloudnc/ngx-sub-form

如果您使用库,则遇到的错误永远不会发生,因为我们像Tobi那样处理这种情况。

从您的角度来看,只需要您扩展一堂课而已!还将为您提供输入安全性,访问嵌套错误等的机会。

我还回答了一个有关如何构建复杂/嵌套/子表单的问题,您可能对此感兴趣https://stackoverflow.com/a/56375605/2398593

答案 2 :(得分:-1)

角度应用程序状态更改可能来自:

  • 活动
  • XHR
  • 计时器

但是当您使用控制值访问器编写值时,您需要通过ChangeDetectorRefdetectChanges()

告知角度更新更改

您应该看一下Pascal Precht的文章Who notifies Angular