【问题标题】:Angular ExpressionChangedAfterItHasBeenCheckedError when passing data from Parent Component to Child Component using Map使用 Map 将数据从父组件传递到子组件时出现 Angular ExpressionChangedAfterItHasBeenCheckedError
【发布时间】:2021-01-15 09:43:45
【问题描述】:

我有一个非常简单的设置。 我在 app.component.html 中有一个输入字段,用户可以在其中输入字符串。我将该字符串添加到 Map。我将该地图传递给子组件。我正在使用 @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);
  }

购物车组件.html

<div *ngFor="let article of this.shoppingItems.entries()" class="shopping-cart-item">
  <div class="article-name">{{ article[0] }}</div>
</div>

Shopping-Cart-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)

【问题讨论】:

  • 你能显示完整的异常信息吗?
  • 在问题中更新了
  • 您是否在孩子中使用推送更改检测?
  • @bryan60 不...我没有

标签: javascript angular typescript angular-changedetection


【解决方案1】:

这几乎肯定是由于在模板中使用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>

【讨论】:

    【解决方案2】:

    问题是 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 处理重复项,因此也在数组中处理重复项,因为我们依赖于 Map。

    试试看。

    注意:还有一个重要的高级主题称为变更检测

    【讨论】:

      【解决方案3】:
      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();
                    }
      

      【讨论】:

        【解决方案4】:
        updateName(){
        
        setTimeout(() => {
        this.shoppingItems.set('test', 1);   
        },
         0);
             
        }
        

        试试这个,你应该不会再出现这个错误(将代码放在 setTimeout 中的函数中)

        【讨论】:

          猜你喜欢
          • 2020-04-05
          • 2017-06-25
          • 1970-01-01
          • 2018-04-16
          • 1970-01-01
          • 2019-02-27
          • 2017-04-20
          相关资源
          最近更新 更多