Angular is a powerful framework for building dynamic web applications. However, developers often encounter an error known as "ExpressionChangedAfterItHasBeenCheckedError". This error can be confusing, especially for those new to Angular’s change detection mechanism.
In this article, we will explore:
By the end of this article, you will have a solid understanding of how to avoid this issue and ensure smoother Angular development.
The ExpressionChangedAfterItHasBeenCheckedError occurs when Angular detects that a binding value has changed after change detection has already run. Angular has a mechanism to verify that bindings remain stable after the initial change detection cycle. If a value changes unexpectedly, Angular throws this error.
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
Previous value: 'false'. Current value: 'true'.
This error often happens in development mode because Angular runs change detection twice to ensure values do not change unexpectedly.
This error occurs due to how Angular handles change detection. Some common scenarios that trigger it include:
ngAfterViewInit
or ngAfterContentInit
Lifecycle hooks like ngAfterViewInit
and ngAfterContentInit
run after the first change detection cycle. If you modify a bound property inside these hooks, it triggers this error.
@Component({
selector: "app-example",
template: `<p>{{ value }}</p>`,
})
export class ExampleComponent {
value = false;
ngAfterViewInit() {
this.value = true; // This will cause the error
}
}
async
OperationIf an async
operation like setTimeout
or an HTTP request updates a value after the first change detection cycle, the error occurs.
ngOnInit() {
setTimeout(() => {
this.value = true;
}, 0);
}
Using ElementRef
or Renderer2
to modify the DOM can cause this error because Angular is not aware of these changes.
There are several strategies to prevent this error from occurring:
setTimeout
or Promise.resolve()
Wrapping the update inside setTimeout
or Promise.resolve()
defers the change to the next JavaScript event loop cycle.
ngAfterViewInit() {
setTimeout(() => {
this.value = true;
});
}
ChangeDetectorRef.detectChanges()
Injecting ChangeDetectorRef
and calling detectChanges()
forces Angular to check for changes manually.
constructor(private cdr: ChangeDetectorRef) {}
ngAfterViewInit() {
this.value = true;
this.cdr.detectChanges();
}
ChangeDetectorRef.markForCheck()
If your component uses OnPush change detection, call markForCheck()
to schedule a change detection cycle.
constructor(private cdr: ChangeDetectorRef) {}
ngAfterViewInit() {
this.value = true;
this.cdr.markForCheck();
}
BehaviorSubject
or EventEmitter
for State UpdatesUsing BehaviorSubject
(from RxJS) or EventEmitter
ensures updates are properly handled in an observable manner.
value$ = new BehaviorSubject<boolean>(false);
ngAfterViewInit() {
this.value$.next(true);
}
Avoid expressions in templates that modify values directly. Instead, compute values in the component class.
<p>{{ Math.random() }}</p>
randomValue = Math.random();
<p>{{ randomValue }}</p>
If you encounter this error, try the following approaches:
Check the error message to see which property has changed. Locate the lifecycle hook or function where the update occurs.
setTimeout
or Promise.resolve()
If the change happens in ngAfterViewInit
, wrap it inside setTimeout()
or Promise.resolve()
.
ChangeDetectorRef.detectChanges()
If necessary, force Angular to check for updates manually by injecting ChangeDetectorRef
.
If changes are happening unexpectedly, consider restructuring the component. Move state updates to ngOnInit()
instead of ngAfterViewInit()
.
The "ExpressionChangedAfterItHasBeenCheckedError" occurs when Angular detects that a bound value has changed unexpectedly after the initial change detection cycle. This happens due to lifecycle hooks, async operations, or direct DOM manipulations.
To prevent and fix this error:
setTimeout
or Promise.resolve()
to defer updatesChangeDetectorRef.detectChanges()
if necessaryBy following these best practices, you can prevent this error and ensure a smoother Angular development experience.