Here are two solutions for you!
1. Adjust ChangeDetectionStrategy to OnPush
In this solution, you're essentially informing Angular:
Stop checking for changes continuously; I will trigger it only when necessary
Update your component to utilize ChangeDetectionStrategy.OnPush
like so:
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChildComponent implements OnInit {
// ...
}
After implementing this, the behavior may seem different. This is because you now need to manually call detectChanges()
in Angular.
this.cdr.detectChanges();
If you want more insight, feel free to explore this article. It provided me with a better understanding of how ChangeDetectionStrategy
operates.
2. Understanding ExpressionChangedAfterItHasBeenCheckedError
Please refer to this informative video about the error (very helpful!). Additionally, here is an extract from this article, focusing on the causes of this error, highlighting segments that aided my comprehension.
The complete article provides concrete code examples corresponding to each point discussed.
The primary issue lies within the Angular lifecycle:
Following every operation, Angular remembers the values utilized during the operation. These values are stored in the oldValues property of the component view.
Upon completing all component checks, Angular proceeds to the next digest cycle. Instead of executing operations, it compares current values to those remembered from the previous digest cycle.
During digest cycles, the following validations are performed:
Verifying that values passed down to child components match the ones used for updating their properties at present time.
Ensuring that values employed for updating DOM elements coincide with what would update these elements accurately and then perform the same action.
Checking all child components.
Consequently, the error arises when compared values differ. According to blogger Max Koretskyi:
The source of concern typically traces back to the child component or a directive.
Moreover, below are real-world scenarios commonly leading to this error:
- Shared services (example)
- Synchronous event broadcasting (example)
- Dynamic component instantiation (example)
In my scenario, the issue stemmed from dynamic component instantiation.
Furthermore, based on personal experience, I strongly advise against utilizing the setTimeout
approach which, in my case, led to an "almost" infinite loop (21 calls that I prefer not elaborating on).
I recommend staying mindful of Angular's life cycles during development, considering how alterations in one component may impact others. With this error, Angular is signaling:
You might be approaching this incorrectly; are you certain about your methodology?
The aforementioned blog also states:
Typically, rectifying the situation involves utilizing the appropriate change detection hook for creating a dynamic component.
A quick guide emphasizing several coding considerations:
(Additional insights will likely be included gradually):
- Avoid modifying parent component values directly from its child components; instead, alter them from their parent counterpart.
- When utilizing
@Input
and @Output
directives, strive to minimize triggering lifecycle adjustments until the component is fully initialized.
- Avert unnecessary invocations of
this.cdr.detectChanges();
as they can potentially lead to additional errors, particularly when dealing with substantial dynamic data.
- When employing
this.cdr.detectChanges();
becomes imperative, confirm that variables (@Input, @Output, etc.
) being leveraged are populated/initiated within the appropriate detection hook (OnInit, OnChanges, AfterView, etc.
).
- Whenever feasible, opt to remove rather than conceal; this aligns with points 3 and 4 (same viewpoint for angulardart).
- Lay off incorporating intricate logic within setters annotated with
@Input
; such logic executes prior to ngAfterViewInit</code and can readily engender the issue. In instances where it's essential, consider relocating said logic into the <code>ngOnChanges
method.
Additionally
To attain a comprehensive understanding of Angular Lifecycle Hooks, I encourage delving into the official documentation accessible here:
https://angular.io/guide/lifecycle-hooks