[英]Angular ExpressionChangedAfterItHasBeenCheckedError when passing data from Parent Component to Child Component using Map
我有一個非常簡單的設置。 我在 app.component.html 中有一個輸入字段,用戶可以在其中輸入字符串。 我將該字符串添加到 Map<string, number>。 我正在將該地圖傳遞給子組件。 我正在使用 @Input() 訪問子組件中的地圖並對其進行迭代並顯示子組件中的值。
app.component.html -> 父組件
<div class="shopping-cart">
<app-shopping-cart-item [shoppingItems]="shoppingItems"></app-shopping-cart-item>
</div>
<p>
<button class="btn btn-primary" (click)="updateName()">Update Name</button>
</p>
app.component.ts
shoppingItems = new Map<string, number>();
updateName(){
this.shoppingItems.set('test', 1);
}
購物車-component.html
<div *ngFor="let article of this.shoppingItems.entries()" class="shopping-cart-item">
<div class="article-name">{{ article[0] }}</div>
</div>
購物車-Item.component.ts
@Input()
shoppingItems;
問題是當我在輸入值(在父組件中)輸入一個值並單擊更新時,它會更新子組件中顯示的值。 但我得到一個 ExpressionChangedAfterItHasBeenCheckedError。
我知道當我們沒有單向數據流時會出現此錯誤。 但我不明白為什么在這種情況下會出現此錯誤。
core.js:6210 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: '[object Map Iterator]'. Current value: '[object Map Iterator]'.
at throwErrorIfNoChangesMode (core.js:8129)
at bindingUpdated (core.js:19991)
at Module.ɵɵproperty (core.js:21142)
at ShoppingCartItemComponent_Template (shopping-cart-item.component.html:3)
at executeTemplate (core.js:12059)
at refreshView (core.js:11906)
at refreshComponent (core.js:13358)
at refreshChildComponents (core.js:11635)
at refreshView (core.js:11958)
at refreshComponent (core.js:13358)
這幾乎肯定是由在模板中使用entries()
函數引起的。 我無法詳細說明為什么會導致此問題,因為我不太了解Map
的內部工作原理,但我假設它與創建的新數組(或迭代器)類似每次調用該函數時,都會在每個更改檢測周期調用該函數,這使 Angular 相信在按鈕單擊引起的更改檢測周期中發生了某些變化。 或者也許 Angular 不能很好地處理迭代器。
無論哪種方式,使用keyvalue
管道進行迭代應該可以解決問題,因為它只會在需要時更改數組:
<div *ngFor="let article of this.shoppingItems | keyvalue" class="shopping-cart-item">
<div class="article-name">{{ article.key }}</div>
</div>
問題是 entry() 函數。 該函數在內部創建了一個可迭代對象,每次向其添加內容時它都會更改。 從組件視圖調用函數被稱為不好的做法,因此如果您想在父組件中維護 Map,那么我建議您子組件從父組件接收數組而不是 Map。 為此,您必須在父組件中創建一個數組屬性作為 Map 並將其傳遞給子組件。 每次執行 updateName 函數時,您都應該同時維護 Map 和 Array。
shoppingItems = new Map<string, number>();
childArray = [];
updateName(){
this.shoppingItems.set('test', 1);
this.childArray.push(this.shoppingItems.get('test'));
}
一個小廣告:你的 updateName() 函數有一個重要的情況。 首先,它不是一個參數化函數,因為每次執行它時都在添加相同的東西。 因此,Map 是一種結構,它不允許您添加多次相同的鍵,在本例中為“測試”,以便每次都替換相同的鍵值。 除此之外,當您想參數化該函數時,您必須確保在該鍵已經添加到數組中之前,以避免重復項並替換索引(如果適用)。 僅供參考。 那么,接下來。
然后,我們也必須更改子視圖和父視圖:
家長:
<div class="shopping-cart">
<app-shopping-cart-item [shoppingItems]="childArray"></app-shopping-cart-item>
</div>
<p>
<button class="btn btn-primary" (click)="updateName()">Update Name</button>
</p>
孩子:
<div *ngFor="let article of this.shoppingItems" class="shopping-cart-item">
<div class="article-name">{{ article }}</div>
</div>
因此,以這種方式,我們使用 Map 處理重復項,因此也在 Array 中處理重復項,因為我們依賴於 Map。
嘗試一下。
注意:還有另一個重要且高級的主題,稱為更改檢測。
Try like this.
import { Component, ElementRef, OnInit, ViewChild, ChangeDetectorRef, ViewRef } from '@angular/core';
constructor(
private dtr: ChangeDetectorRef,
) {
}
// write this code where you want to detect change in browser
if (this.dtr && !(this.dtr as ViewRef).destroyed) {
this.dtr.detectChanges();
}
updateName(){
setTimeout(() => {
this.shoppingItems.set('test', 1);
},
0);
}
試試這個,你應該不會再得到那個錯誤(將代碼放在 setTimeout 的函數中)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.