【问题标题】:ngFor depends on another ngFor to finish rendering - getting ExpressionChangedAfterItHasBeenCheckedError [Angular 2]ngFor 依赖另一个 ngFor 来完成渲染 - 获取 ExpressionChangedAfterItHasBeenCheckedError [Angular 2]
【发布时间】:2017-10-08 05:12:16
【问题描述】:

我有两个组件列表,它们使用两个不同服务提供的数据:

  • 第一个列表包含一些具有随机高度的组件(在渲染之前我不知道它们的高度)
  • 第二个列表中的组件的高度必须根据第一个列表中组件的高度计算。

这两个列表由同一个组件使用两个 *ngFor 循环生成,并且服务数据是从另一个子组件更改的。

问题在于,当模型数据发生变化时,两个 ngFor 循环都尝试更新模板,但第二个循环失败,因为它依赖于尚未准备好的第一个 ngFor。

我确实尝试使用 ChangeDetectorRef.detectChanges() 或侦听包含第一个列表组件的 QueryList 发出的更改,但我仍然收到 ExpressionChangedAfterItHasBeenCheckedError。

实际情况稍微复杂一些,但下面是代码的简化版本:

https://embed.plnkr.co/sr9k0wLQtyWSATiZuqaK/

提前致谢,这是我关于 stackoverflow 的第一个问题 :)

【问题讨论】:

标签: angular angular2-changedetection


【解决方案1】:

我会避免使用在模板中计算高度的方法。相反,我会准备数据以供查看:

<second-cmp 
  *ngFor="let cmp of generatedData" 
 [model]="cmp" 
 [height]="cmp.height"> <------------ already calculated value

然后我会订阅QueryList.changes 来跟踪生成元素的变化并在那里计算height

constructor(
  ...,
  private cdRef: ChangeDetectorRef) {}

ngAfterViewInit() {
    this.components.changes.subscribe(components => {
      const renderedCmps =  components.toArray();
      this.generatedData.forEach((x, index) => {
        switch (index) {
          case 0: // first
            x.height = renderedCmps[0].height / 2;
            break;
          case this.modelData.length: // last
            x.height = (renderedCmps[renderedCmps.length - 1].height / 2);
            break;
          default: // in-between
            x.height = (renderedCmps[index - 1].height + renderedCmps[index].height) / 2
        }
      });
      this.cdRef.detectChanges();
    });
}

+ 'px' 是多余的,因为我们可以在样式绑定中指定它:

[style.height.px]="height"

Plunker Example

【讨论】:

    【解决方案2】:

    优秀的回复@yurzui,就像一个魅力!

    在模型数据上设置 height 属性也消除了单独绑定它的必要性,因为我已经将整个模型的引用传递给 *ngFor 中的组件。

    <second-cmp 
      *ngFor="let cmp of generatedData" 
     [model]="cmp"> <!-- the height doesn't need to be binded separately anymore -->
    

    @Component({
    selector: 'second-cmp',
    template: `
      <li>
        <div class="item" [style.height.px]="model.height">Calculated height</div>
      </li>`
    )}
    export class Second {
      @Input('model') model;
      // @Input('height') height; <-- removed
    }
    

    Final solution

    【讨论】:

    • 好改进)
    猜你喜欢
    • 1970-01-01
    • 2018-02-03
    • 1970-01-01
    • 2016-08-06
    • 1970-01-01
    • 1970-01-01
    • 2017-11-18
    相关资源
    最近更新 更多