Her you go two solutions!
1. Modify ChangeDetectionStrategy to OnPush
For this solution, you'r basically telling angular:
Stop checking for changes; i'll do it only when i know is necessary
The quick fix:
Modify your component so it'll use ChangeDetectionStrategy.OnPush
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent implements OnInit {
// ...
}
With this, things don’t seem to work anymore. That's because from now on you'll have to make Angular call the detectChanges() manually.
this.cdr.detectChanges();
Here's a link that helped me understand ChangeDetectionStrategy right:
https://alligator.io/angular/change-detection-strategy/
2. Understanding ExpressionChangedAfterItHasBeenCheckedError
Here is a small extract from tomonari_t answer about the causes for this error, I've tried to include only the parts that helped me to understand this.
The full article shows real code examples about every point shown here.
The root cause is angular lifecycle's:
After each operation Angular remembers what values it used to perform
an operation. They are stored in the oldValues property of the
component view.
After the checks have been done for all components Angular then starts
the next digest cycle but instead of performing operations it compares the current values with the ones it remembers from
the previous digest cycle.
The following operations are the being checked at digest cycle's:
check that values passed down to the child components are the same as
the values that would be used to update properties of these components
now.
check that values used to update the DOM elements are the same as
the values that would be used to update these elements now perform the
same.
checks for all child components
And so, the error is thrown when the compared values are different., blogger Max Koretskyi stated:
The culprit is always the child component or a directive.
And finally here are some real world samples that usually cause this error:
- Shared services
- Synchronous event broadcasting
- Dynamic component instantiation
Every sample can be found here (plunkr), in my case the problem was a dynamic component instantiation.
Also, by my own experience i strongly recommend everyone to avoid the setTimeout solution, in my case caused an "almost" infinite loop (21 calls which i'm not willing to show you how to provoke them),
I would recommend to always keep in mind Angular life-cycle's so you can take in account how they would be affected every time you modify another component's value. With this error Angular is telling you:
You're maybe doing this the wrong way, are you sure you're right?
The same blog also says:
Often, the fix is to use the right change detection hook to create a dynamic component
A short guide for me is to consider at least the following things while coding:
(i'll try to complement it over time):
- Avoid modifying parent component values from it's child's components,
instead: modify them from its parent.
- When you use
@Input and @Output directives try to avoid triggering lyfecycle changes unless the component is completely initialized.
- Avoid unnecessary calls of
this.cdr.detectChanges(); they can trigger more errors, specially when you'r dealing with a lot of dynamic data
- When the use of
this.cdr.detectChanges(); is mandatory make sure that the variables (@Input, @Output, etc) being used are filled/initialized at right detection hook (OnInit, OnChanges, AfterView, etc)
- When possible, remove rather than hide, this is related to point 3 and 4. (Update: I noticed today that Angular recommendation to remove rather than hide is gone from their webpage, here's the same quote but for angulardart)
Also
If you want to fully understand Angular Life Hook i recommend you to read the official documentation here: