当表单拆分为子组件时无法基于 Select 字段重置输入值

Can't Reset Input Value Based on Select Field When Form Split into Child Components

我正在显示和隐藏一个基于同一 FormGroup 中另一个 FormControl 的 FormControl,它使用下面的方法工作,但是当我尝试重置被隐藏的输入时,所以在提交时它的值没有发送我得到以下错误。

组件方法

public hasOtherFundingSource(index: number) {
  const formGroup = this.overviewFunds.at(index);
  const source = formGroup.get('source').value;

  if (source !== 'other') {
    formGroup.get('other_source_desc').reset(); // Throws error
    return false;
  } else {
    return true;
  }
}

错误

ExampleComponent.html:12 ERROR Error: 
ExpressionChangedAfterItHasBeenCheckedError: 
Expression has changed after it was checked. 
Previous value: 'true'. Current value: 'false'.

在@yurzui 的帮助下,我创建了一个显示错误的 plunker。只需在 select 字段中将 other 更改为 another 并观察控制台注销错误。该错误似乎与将 FormGroup 拆分为子组件以减少每个 class.

中的大小和逻辑有关

如果您查看创建的第一个 plunker,当表单封装在应用程序组件中时不会发生错误。

您应该避免在每次视图检查时执行的函数中产生任何副作用

<div *ngIf="hasOtherFundingSource(i)">

hasOtherFundingSource 函数将在每个应用程序 tick 的开发模式下执行两次。

NgControlStatus 检查验证状态的指令。

第一次检查您的表格是否有效

之后您调用 formGroup.get('other_source_desc').reset(); 并且状态变为 invalid

那么 angular 就是 运行 view.checkNoChanges() 你得到 ExpressionChangedAfterItHasBeenCheckedError

所以根据https://angular.io/docs/ts/latest/guide/template-syntax.html#!#no-visible-side-effects

没有可见的副作用

模板表达式不应更改目标值以外的任何应用程序状态 属性。

This rule is essential to Angular's "unidirectional data flow" policy. You should never worry that reading a component value might change some other displayed value. The view should be stable throughout a single rendering pass.

为了解决您的问题,我执行了以下操作:

1) 将 ngModelChange 事件添加到 select 控件

<select formControlName="source" (ngModelChange)="sourceChanged($event, i)">

2) 将副作用从 hasOtherFundingSource 移至 sourceChanged 函数

sourceChanged(value: string, index: number) {
  if (value !== 'other') {
      const formGroup = this.overviewFunds.at(index);
      formGroup.get('other_source_desc').reset();
  }
}

Modified Plunker