Angular 4 ExpressionChangedAfterItHasBeenCheckedError

in ParentComponent =>

ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: ''. Current value: '[object Object]'. at viewDebugError (vendor.bundle.js:8962) at expressionChangedAfterItHasBeenCheckedError (vendor.bundle.js:8940) 

Html Parent Component

 <div> <app-child-widget [allItems]="allItems" (notify)="eventCalled($event)"></app-child-widget> <div> 

Parent component

 export class ParentComponent implements OnInit { returnedItems: Array<any> = []; allItems: Array<any> = []; constructor( ) { } ngOnInit() { this.allItems = // load from server... } eventCalled(items: Array<any>): void { this.returnedItems = items; } } 

Child component

 @Component({ selector: 'app-child-widget', templateUrl: 'child.component.html', styleUrls: ['./child.component.css'] }) export class ChildComponent implements OnInit { @Output() notify: EventEmitter<any> = new EventEmitter(); @Input() private allItems: Array<any>; constructor() { } ngOnInit() { doSomething(); } doSomething() { this.notify.emit(allItems); } } 
+8
angular angular-components
source share
2 answers

Article Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedError error explains this behavior in great detail.

Cause

Your problem is very similar to this one , but instead of updating the parent property through the service you are updating, via synchronous broadcast of events. Here is a quote from a related answer :

In an Angular loop, certain operations are performed on a child element of a directive. One such operation is updating the input and calling ngOnInit to bind the life cycle to child directives / components. what is important that these operations are carried out in a strict order:

  • Update Inputs
  • NgOnInit call

So, in your case, Angular updated the binding of input allItems to the child component, and then called onInit child component, which caused the update of allItems parent component. You now have inconsistent data. The parent component has one meaning, and the other has another. If Angular continues to sync changes, you get an infinite loop. Therefore, during the next Angular change detection cycle, it was discovered that allItems was changed and an error was allItems .

Decision

This seems to be a design flaw when updating details from both the parent and the child component. If this is not the case, you can solve the problem by emitting the event asynchronously as follows:

 export class ChildComponent implements OnInit { @Output() notify: EventEmitter<any> = new EventEmitter(true); ^^^^^^------------- 

But you have to be very careful. If you use any other hook of type ngAfterViewChecked that is called in each digest cycle , you will get a circular dependency!

+10
source share

In my case, I changed the state of my data - this answer requires you to read the explanation of the digest cycle for AngularInDepth.com - inside the html level all I had to do was change the way this data was processed:

 <div>{{event.subjects.pop()}}</div> 

in

 <div>{{event.subjects[0]}}</div> 

Summary: instead of popping - which returns and deletes the last element of the array, thus changing the state of my data - I used the usual way to get my data without changing its state - preventing an exception.

0
source share

All Articles