2017-10-08 101 views
2

我必須使用由兩種不同的服務提供的數據組件的兩個列表:ngFor依賴於另一個ngFor完成渲染 - 讓ExpressionChangedAfterItHasBeenCheckedError [角2]

  • 第一列表包含隨機高度一些組件(我在渲染之前不知道它們的高度)
  • 第二個列表中的組件必須根據第一個列表中的組件的高度計算它們的高度。

這兩個列表都是由同一個組件使用兩個* ngFor循環生成的,而服務數據是從另一個子組件生成的。

問題是,當模型數據發生變化時,兩個ngFor循環嘗試更新模板,但第二個失敗,因爲它依賴於尚未準備好的第一個ngFor。我嘗試使用ChangeDetectorRef.detectChanges()或偵聽包含第一個列表組件的QueryList發出的更改,但我仍然收到ExpressionChangedAfterItHasBeenCheckedError。

真正的情況是比較複雜一點,但這裏的代碼是什麼樣子的簡化版本:提前

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

謝謝,這是我的計算器:)

+0

你會發現(HTTPS這篇文章有用[你需要了解的'ExpressionChangedAfterItHasBeenCheckedError'錯誤一切]://博客。 angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4) –

回答

2
第一個問題

我會避免使用模板內計算高度的方法。相反,我將用於視圖準備數據:

<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

1

優秀的響應@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

+0

好改進) – yurzui