Angular 使用 Map 将数据从父组件传递到子组件时出现 ExpressionChangedAfterItHasBeenCheckedError
Angular ExpressionChangedAfterItHasBeenCheckedError when passing data from Parent Component to Child Component using Map
我有一个非常简单的设置。
我在 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);
}
购物车-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)
updateName(){
setTimeout(() => {
this.shoppingItems.set('test', 1);
},
0);
}
试试这个,你应该不会再得到那个错误(将代码放在 setTimeout 的函数中)
这几乎肯定是模板中使用了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>
问题出在 entries() 函数上。该函数在内部创建了一个可迭代的 object ,每次您向其中添加内容时它都会发生变化。从组件视图调用函数是一种不好的做法,因此如果您想在 parent 组件中维护 Map 那么我建议您 child 组件从 parent 接收一个数组的地图。为此,您必须在 parent 组件中创建一个数组 属性 作为 Map 并将其传递给 child 组件。每次执行 updateName 函数时,您都应该同时维护 Map 和 Array。
shoppingItems = new Map<string, number>();
childArray = [];
updateName(){
this.shoppingItems.set('test', 1);
this.childArray.push(this.shoppingItems.get('test'));
}
一个小广告:
您的 updateName() 函数有一个重要的情况。首先,它不是参数化函数,因为每次执行它时都会添加相同的东西。因此,Map 是一种结构,不允许您多次添加相同的键,在本例中为 'test',因此每次都会替换相同的键值。除此之外,当您想参数化函数时,必须先确定该键是否已添加到数组中,以避免重复项并在适用时替换索引。
仅供参考。 那么,接下来。
然后,我们也必须更改 child 和 parent 视图:
Parent:
<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>
Child:
<div *ngFor="let article of this.shoppingItems" class="shopping-cart-item">
<div class="article-name">{{ article }}</div>
</div>
因此,我们以这种方式使用 Map 处理重复项,因此也在数组中处理重复项,因为我们依赖于 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();
}
我有一个非常简单的设置。
我在 app.component.html 中有一个输入字段,用户可以在其中输入字符串。我将该字符串添加到 Map
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)
updateName(){
setTimeout(() => {
this.shoppingItems.set('test', 1);
},
0);
}
试试这个,你应该不会再得到那个错误(将代码放在 setTimeout 的函数中)
这几乎肯定是模板中使用了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>
问题出在 entries() 函数上。该函数在内部创建了一个可迭代的 object ,每次您向其中添加内容时它都会发生变化。从组件视图调用函数是一种不好的做法,因此如果您想在 parent 组件中维护 Map 那么我建议您 child 组件从 parent 接收一个数组的地图。为此,您必须在 parent 组件中创建一个数组 属性 作为 Map 并将其传递给 child 组件。每次执行 updateName 函数时,您都应该同时维护 Map 和 Array。
shoppingItems = new Map<string, number>();
childArray = [];
updateName(){
this.shoppingItems.set('test', 1);
this.childArray.push(this.shoppingItems.get('test'));
}
一个小广告: 您的 updateName() 函数有一个重要的情况。首先,它不是参数化函数,因为每次执行它时都会添加相同的东西。因此,Map 是一种结构,不允许您多次添加相同的键,在本例中为 'test',因此每次都会替换相同的键值。除此之外,当您想参数化函数时,必须先确定该键是否已添加到数组中,以避免重复项并在适用时替换索引。 仅供参考。 那么,接下来。
然后,我们也必须更改 child 和 parent 视图:
Parent:
<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>
Child:
<div *ngFor="let article of this.shoppingItems" class="shopping-cart-item">
<div class="article-name">{{ article }}</div>
</div>
因此,我们以这种方式使用 Map 处理重复项,因此也在数组中处理重复项,因为我们依赖于 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();
}