簡體   English   中英

使用 Map 將數據從父組件傳遞到子組件時出現 Angular ExpressionChangedAfterItHasBeenCheckedError

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM