简体   繁体   English

ngFor依赖于另一个ngFor完成渲染-获取ExpressionChangedAfterItHaHasBeenCheckedError [Angular 2]

[英]ngFor depends on another ngFor to finish rendering - getting ExpressionChangedAfterItHasBeenCheckedError [Angular 2]

I have two lists of components that use data provided by two different services: 我有两个使用两个不同服务提供的数据的组件列表:

  • the first list contains some components with random heights (I don't know their heights until they are rendered) 第一个列表包含一些具有随机高度的组件(在渲染它们之前我不知道它们的高度)
  • the components in the second list must have their heights calculated based on the heights of the ones in the first list. 第二个列表中的组件的高度必须根据第一个列表中的组件的高度计算得出。

Both lists are generated by the same component using two *ngFor loops, and the services data is changed from another child component. 这两个列表是由同一组件使用两个* ngFor循环生成的,并且服务数据已从另一个子组件更改。

The problem is that when the model data changes, both ngFor loops try to update the template, but the second one fails because it is dependent upon the first ngFor which is not yet ready. 问题在于,当模型数据更改时,两个ngFor循环都尝试更新模板,但是第二个失败,因为它依赖于尚未准备就绪的第一个ngFor。

I did try to use ChangeDetectorRef.detectChanges() or to listen for the changes emitted by the QueryList containing the first list's components, but I'm still getting ExpressionChangedAfterItHasBeenCheckedError. 我确实尝试使用ChangeDetectorRef.detectChanges()或侦听包含第一个列表的组件的QueryList发出的更改,但是我仍然收到ExpressionChangedAfterItHaHasBeenCheckedError。

The real scenario is a bit more complicated, but here's a simplified version of what the code looks like: 实际情况要复杂一些,但这是代码的简化版本:

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

Thanks in advance, this is my first question on stackoverflow :) 预先感谢,这是我关于stackoverflow的第一个问题:)

I would avoid using methods that calculate height within template. 我会避免使用在模板中计算高度的方法。 Instead i would prepare data for view: 相反,我会准备数据供查看:

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

Then i would subscribe to QueryList.changes to track changes of generated elements and calculate height there: 然后,我将订阅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' is redundant because we can specify it within style binding: + 'px'是多余的,因为我们可以在样式绑定中指定它:

[style.height.px]="height"

Plunker Example 柱塞示例

Excellent response @yurzui , works like a charm! @yurzui的出色响应,就像一个魅力!

Setting the height property on the model data also removes the necessity of binding it separately since I'm already passing the reference of the entire model to the component in the *ngFor. 在模型数据上设置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 最终解决方案

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM