I have a very simple setup. I have an input field in app.component.html in which the user can type in a string. I am adding that string to a Map<string, number>. I am passing that map to child component. I am accessing the map in child component using @Input() and iterating over it and displaying the values in child component.
app.component.html -> Parent Component
<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);
}
shopping-cart-component.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;
The issue is when I enter a value in the input value (in parent) and click update then it updates the values displayed in the child component. But I get a ExpressionChangedAfterItHasBeenCheckedError.
I know that we get this error when we don't have unidirectional data flow. But I don't understand why I get this error in this scenario.
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)
This is almost definitely caused by using the
entries()
function in template. I can't speak to the specifics of why this is causing the issue, as I don't know the inner workings ofMap
that well, but I'm assuming it's something along the lines of a new array (or Iterator) gets created everytime that function is called, and that function is called on every change detection cycle, which is making angular believe something is changing during the change detection cycle caused by the button click. Or perhaps Angular doesn't handle Itertors very well.either way, using the
keyvalue
pipe to iterate instead should solve the problem, as it will only change the array when it needs to: