Question
In Angular, why does this component throw an ExpressionChangedAfterItHasBeenCheckedError even though it only updates a simple bound property after the view is initialized?
@Component({
selector: 'my-app',
template: `<div>I'm {{ message }}</div>`,
})
export class App {
message: string = 'loading :(';
ngAfterViewInit() {
this.updateMessage();
}
updateMessage() {
this.message = 'all done loading :)';
}
}
The error is:
EXCEPTION: Expression 'I\'m {{message}} in App@0:5' has changed after it was checked.
Previous value: 'I\'m loading :('.
Current value: 'I\'m all done loading :)'
Why does Angular complain here, and what is the correct way to think about this lifecycle behavior?
Short Answer
By the end of this page, you will understand why Angular throws ExpressionChangedAfterItHasBeenCheckedError, how change detection interacts with lifecycle hooks like ngAfterViewInit, and how to update component state safely without confusing Angular’s view consistency checks.
Concept
ExpressionChangedAfterItHasBeenCheckedError happens when Angular detects that a value used in the template changed after Angular already checked it during the same change detection cycle.
In the example, message starts as:
'loading :('
Angular renders and checks the template:
<div>I'm {{ message }}</div>
Then ngAfterViewInit() runs, and inside it you change message to:
'all done loading :)'
That means Angular just finished checking the view with one value, and then the value changed immediately afterward during the same cycle. In development mode, Angular performs an extra verification pass to catch exactly this kind of unstable state.
This error is useful because it warns you that the UI is not stable during rendering. If Angular allowed values to keep changing while a check was in progress, the DOM could become inconsistent or harder to reason about.
Why this matters
Angular’s rendering model expects template-bound values to be stable during a check. Lifecycle hooks are not all equally safe for changing data:
ngOnInit()is generally safe for initializing component state.
Mental Model
Think of Angular change detection like a teacher collecting exam papers.
- First, the teacher reads the answer sheet.
- Then, just after the sheet is marked as checked, the student tries to erase an answer and write a new one.
- The teacher says: "Wait, this was already checked. Why did it change now?"
That is what Angular is doing.
ngAfterViewInit() happens after the view exists and has been checked. If you change a displayed value there, Angular sees that the already-checked output no longer matches the current state.
A good mental rule is:
- Before check: prepare values
- During/after view check: avoid changing values that the template already used
- If you must react after view init: schedule the update for a later turn or trigger change detection intentionally
Syntax and Examples
The key Angular lifecycle hooks involved here are:
ngOnInit() {
// initialize component state
}
ngAfterViewInit() {
// view is created and child views are available
}
Safe initialization in ngOnInit
If the value does not depend on the rendered view, initialize it earlier:
@Component({
selector: 'my-app',
template: `<div>I'm {{ message }}</div>`,
})
export class App {
message = 'loading :(';
ngOnInit() {
this.message = 'all done loading :)';
}
}
This works because Angular has not finished checking the view yet.
Delaying the update until the next task
If you truly need ngAfterViewInit(), you can defer the change:
@Component({
selector: 'my-app',
template: ,
})
{
message = ;
() {
( {
. = ;
});
}
}
Step by Step Execution
Consider this code:
@Component({
selector: 'my-app',
template: `<div>I'm {{ message }}</div>`,
})
export class App {
message = 'loading :(';
ngAfterViewInit() {
this.message = 'all done loading :)';
}
}
Here is what happens step by step in development mode:
-
Angular creates the component instance.
messageis'loading :('
-
Angular begins change detection.
- It evaluates the template expression
{{ message }} - The template result is
I'm loading :(
- It evaluates the template expression
-
Angular creates and initializes the view.
-
Angular runs
ngAfterViewInit().- Your code changes
messageto'all done loading :)'
- Your code changes
Real World Use Cases
This issue commonly appears in real Angular apps when developers change template-bound values too late in the lifecycle.
Common scenarios
-
Loading indicators
- Setting
loading = falseinsidengAfterViewInit()after the template already showedtrue
- Setting
-
ViewChild-based setup
- Reading a child component or DOM element in
ngAfterViewInit()and then changing text, classes, or flags used in the template
- Reading a child component or DOM element in
-
Dynamic layout logic
- Measuring element size after render and then changing template-bound layout values immediately
-
Form setup
- Updating validation state or field flags after the view was already checked
-
Conditional rendering
- Toggling
*ngIfordisabledflags from post-view hooks without deferring the change
- Toggling
Example: spinner state
loading = true;
ngAfterViewInit() {
. = ;
}
Real Codebase Usage
In real codebases, developers usually handle this problem by choosing the correct lifecycle hook or by structuring state updates more clearly.
Common patterns
1. Initialize state early
Use ngOnInit() for values that do not depend on the DOM or child views.
ngOnInit() {
this.isReady = true;
}
2. Use guard clauses around view-dependent logic
When ngAfterViewInit() is needed, keep it focused on view access:
ngAfterViewInit() {
if (!this.chartContainer) return;
this.initializeChart();
}
Then separate UI state updates carefully.
3. Defer view-dependent state changes
ngAfterViewInit() {
Promise.resolve().then(() => {
this. = ;
});
}
Common Mistakes
1. Changing bound values in ngAfterViewInit() without needing to
Broken example:
ngAfterViewInit() {
this.title = 'Ready';
}
If title does not depend on the view, move it to ngOnInit().
2. Using setTimeout() as the first solution every time
This works, but it can hide design problems.
ngAfterViewInit() {
setTimeout(() => {
this.title = 'Ready';
});
}
Use it only when the update genuinely must happen after the current cycle.
3. Calling detectChanges() everywhere
Broken style:
ngAfterViewInit() {
this.message = 'Done';
this..();
}
Comparisons
| Concept | When it runs | Safe for normal state initialization? | Typical use |
|---|---|---|---|
constructor | When the class is created | Usually no for app logic | Dependency injection, basic setup |
ngOnInit | After Angular sets input properties | Yes | Initialize component data |
ngAfterViewInit | After the component view is created and checked | Often no for immediate bound-value changes | Access @ViewChild, DOM-dependent logic |
ngOnInit() vs ngAfterViewInit()
- Use
ngOnInit()when the value can be decided from inputs, services, or plain logic.
Cheat Sheet
Quick rules
ExpressionChangedAfterItHasBeenCheckedErrormeans a template-bound value changed after Angular checked it.- This usually appears in development mode.
- A common trigger is updating bound state in
ngAfterViewInit().
Safe choices
ngOnInit() {
this.value = 'ready';
}
Risky choice
ngAfterViewInit() {
this.value = 'ready'; // may throw
}
If post-view update is required
ngAfterViewInit() {
setTimeout(() => {
this.value = 'ready';
});
}
or
ngAfterViewInit() {
this.value = ;
..();
}
FAQ
Why does Angular throw ExpressionChangedAfterItHasBeenCheckedError?
Because a value used in the template changed after Angular had already checked that value in the current change detection cycle.
Why does this often happen in ngAfterViewInit()?
Because ngAfterViewInit() runs after the component view has been created and checked, so changing a bound property there can make the checked output immediately outdated.
Is this error only in development mode?
Usually yes. Angular dev mode performs an extra verification pass. Production mode does not throw the same error, but the timing issue still exists.
Should I always fix it with setTimeout()?
No. First ask whether the value should be set in ngOnInit() instead. Use deferring only when the update truly depends on the completed view.
When should I use ChangeDetectorRef.detectChanges()?
Use it when you genuinely need a manual refresh after view-dependent logic or external integration. It should not be the default fix for every case.
Is changing data in ngAfterViewInit() always wrong?
No. It is only a problem when the changed data is already bound in the template and Angular already checked it during that cycle.
What is the simplest fix for the example shown?
Move the message update to if it does not depend on the view.
Mini Project
Description
Build a small Angular component that shows a loading message first and then updates to a ready message after the view is initialized. The project demonstrates both the incorrect approach that causes ExpressionChangedAfterItHasBeenCheckedError and a correct approach that defers the update safely.
Goal
Create a component that updates its message after view initialization without triggering Angular’s expression-changed error.
Requirements
- Create a component with a
messageproperty bound in the template. - Show an initial loading message.
- Update the message after the view is initialized.
- Avoid
ExpressionChangedAfterItHasBeenCheckedError. - Keep the example simple and traceable.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.
Angular @ViewChild Returning Undefined: Lifecycle, Child Components, and Fixes
Learn why Angular @ViewChild can be undefined, when it becomes available, and how to access child components correctly using lifecycle hooks.