This article is valid for the rendering ViewEngine, which preceded Ivy in Angular versions before 10. Some of the explanation may not apply as described in more recent Angular versions that use Ivy.

A recurring theme in the Angular community is the steady stream of stackoverflow questions about the ExpressionChangedAfterItHasBeenCheckedError error. Such questions typically surface because Angular developers are unfamiliar with the mechanics of change detection and the purpose behind the verification pass that triggers this error. Many consider it a defect, but it is not. This mechanism acts as a safeguard against discrepancies between the model and the view, ensuring users are never presented with stale or inconsistent information.

Core change detection steps

An Angular application in operation is essentially a component hierarchy. When change detection runs, each component undergoes a sequence of checks in the order listed:

Additional steps are part of change detection, and the full list is covered in the Everything you need to know about change detection in Angular article.

Following each step, Angular records the values used for that operation, storing them in the component view's oldValues property. Once all components have been processed, Angular initiates a new digest cycle. However, instead of repeating the operations, it compares the current values against those stored from the previous cycle:

  • confirm that values assigned to child components match what would be used for property updates at this moment
  • confirm that values applied to DOM elements match what would be used for those updates now
  • perform the same verification for every child component

Keep in mind that this verification is active only in development mode. The rationale behind this will be explained in the final section of the article.

Consider a parent component A with a child component B. Component A holds properties name and text. The template of A references the name property:

template: '<span>{{name}}</span>'

It also includes B in its template, passing the text property via input binding:

@Component({
    selector: 'a-comp',
    template: `
        <span>{{name}}</span>
        <b-comp [text]="text"></b-comp>
    `
})
export class AComponent {
    name = 'I am A component';
    text = 'A message for the child component`;
    ...
}

When change detection begins, the first step for A is to update bindings. This evaluates the text expression to A message for the child component and forwards it to B, while also saving this value in the view:

view.oldValues[0] = 'A message for the child component';

The subsequent lifecycle hooks from the list are then called.

Then comes the third operation: the {{name}} expression is evaluated to I am A component. The DOM is updated with this text, and the evaluated result is stored in oldValues:

view.oldValues[1] = 'I am A component';

Moving on, Angular checks the child B component in the same manner. With B processed, the primary digest loop concludes.

In development mode, Angular then runs a second digest for verification. Suppose the text property on A was changed to updated text after the value A message for the child component was already passed to B and recorded. During verification, the first check compares the stored value with the current one:

AComponentView.instance.text === view.oldValues[0]; // false
'A message for the child component' === 'updated text'; // false

Since the value has changed, Angular raises the ExpressionChangedAfterItHasBeenCheckedError.

The same logic applies to the third operation. If name was modified after being rendered to the DOM and stored, the same error surfaces:

AComponentView.instance.name === view.oldValues[1]; // false
'I am A component' === 'updated name'; // false

You might be wondering how such changes come about. Let's find out.

Triggers for value modification

The source of the problem is always a child component or a directive. Let's walk through a straightforward demo, then discuss typical scenarios you might face. As you may know, child components and directives can inject their parent components. In our case, B injects A and updates the bound property text. The update occurs in the ngOnInit hook, which fires after bindings are processed, as outlined above:

export class BComponent {
    @Input() text;

    constructor(private parent: AppComponent) {}

    ngOnInit() {
        this.parent.text = 'updated text';
    }
}

This results in the expected error:

Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'A message for the child component'. Current value: 'updated text'.

Now, let's repeat the same for the name property used in A's template:

ngOnInit() {
    this.parent.name = 'updated name';
}

Here, everything works without issue. Why is that?

Looking back at the order of operations, the ngOnInit hook is invoked before the DOM update step, which avoids the error. We need a hook that runs after DOM updates, making ngAfterViewInit suitable:

export class BComponent {
    @Input() text;

    constructor(private parent: AppComponent) {}

    ngAfterViewInit() {
        this.parent.name = 'updated name';
    }
}

This time, we encounter the error:

AppComponent.ngfactory.js:8 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'I am A component'. Current value: 'updated name'.

Real-world situations are often more nuanced, with property updates and DOM changes happening indirectly via services or observables. Yet the underlying cause remains unchanged.

Here are some common patterns that lead to this error.

Shared service

An example is available in this plunker. The setup involves a service used by both a parent and a child component. The child writes a value to the service, which then triggers an update to a property on the parent. This is considered an indirect update because the connection between the child's action and the parent's change isn't immediately obvious.

Synchronous event broadcasting

See this plunker for an illustration. The design has a child emitting an event that a parent listens to. This event leads to updates in several parent properties, which are then used as input bindings for the child—another form of indirect parent property modification.

Dynamic component instantiation

Unlike the prior patterns that affect input bindings, this one causes the DOM update step to throw the error. A working example is in this plunker. Here, a parent component adds a child component dynamically within ngAfterViewInit. Creating a child triggers DOM changes, and since ngAfterViewInit runs after Angular's DOM update, the error appears.

Remedies

Notice the final instruction in the error message:

Expression has changed after it was checked. Previous value:… Has it been created in a change detection hook ?

Frequently, choosing the correct change detection hook for component creation is the fix. For instance, in the dynamic component case above, moving the creation to the ngOnInit hook resolves it. Although the docs say ViewChild is only available after ngAfterViewInit, the children are actually populated when the view is formed, making them accessible sooner.

A quick search will reveal two prevalent solutions: making property updates asynchronous and triggering an additional change detection cycle. While these are presented here with explanations, they are not the recommended approach. Instead, consider restructuring your application, as elaborated in the concluding chapter.

Asynchronous update

What's significant is that both change detection and verification runs are synchronous. So, updating properties asynchronously means the values won't have changed when verification happens, and no error should occur. Let's test this:

export class BComponent {
    name = 'I am B component';
    @Input() text;

    constructor(private parent: AppComponent) {}

    ngOnInit() {
        setTimeout(() => {
            this.parent.text = 'updated text';
        });
    }

    ngAfterViewInit() {
        setTimeout(() => {
            this.parent.name = 'updated name';
        });
    }
}

Indeed, the error is absent. The setTimeout queues a macrotask that executes in a later VM turn. Alternatively, then from a promise runs the update in the current VM turn after synchronous code completes:

Promise.resolve(null).then(() => this.parent.name = 'updated name');

This uses a microtask (from Promise.then) rather than a macrotask. The microtask queue is cleared after synchronous work finishes, so the property change happens post-verification. For a deeper dive into micro and macro tasks in Angular, see I reverse-engineered Zones (zone.js) and here is what I’ve found.

When using EventEmitter, you might enable asynchronous behavior with the true option:

new EventEmitter(true);

Forcing change detection

Another approach is to run a fresh change detection cycle on the parent A after the initial pass but before verification. The fitting location is within ngAfterViewInit, as it fires once all child components have been checked and had chances to modify parent properties:

export class AppComponent {
    name = 'I am A component';
    text = 'A message for the child component';

    constructor(private cd: ChangeDetectorRef) {
    }

    ngAfterViewInit() {
        this.cd.detectChanges();
    }
}

No error here, but this solution has a catch. Forcing change detection on A also triggers checks for all its children, opening the door for another parent property update.

The purpose of the verification loop

Angular enforces what is known as unidirectional data flow from top to bottom. A component positioned deeper in the tree is not permitted to modify properties belonging to a component higher up the hierarchy once those higher-level changes have already been applied. This rule guarantees that the entire component tree is stable after the initial pass of change detection. A tree is deemed unstable when property values change but have not yet been reflected in the components that consume them. In this example, the child component B relies on the parent’s text property. Until these updates are propagated to B, the tree remains unstable. The same principle applies to the DOM, which acts as a consumer of component properties and renders them to the user interface. If these values are not synchronized, the user sees outdated or incorrect content on screen.

Change detection is precisely the synchronization process I outlined earlier—it performs the two operations mentioned at the start. What happens when a child modifies parent properties after that synchronization step is already complete? You are left with an unstable tree, and the outcomes of such a state are unpredictable. In most cases, the user will end up seeing inaccurate information on the page. And tracking down the source of such a problem is notoriously time-consuming.

One might wonder why change detection is not simply run repeatedly until the tree becomes stable. The rationale is straightforward: it could fail to stabilize forever, leading to an infinite process. If a child immediately updates a parent property in response to that very property’s change, you generate a never-ending cycle. While a direct update or dependency is easy to identify in theory, in real applications both the update and the dependency are typically indirect, complicating detection.

Notably, AngularJS lacked this unidirectional data flow constraint and attempted to stabilize the tree anyway. That approach frequently triggered the well-known 10 $digest() iterations reached. Aborting! error. If you search for that message, you will find a huge number of developers struggling with it.

The final question that might come to mind is why this check is active only in development mode. Likely, the reason is that an unstable model is less severe than an outright framework runtime error. It might, after all, resolve itself on a subsequent change detection run. However, during application development, it is far better to be alerted to a potential problem early than to diagnose it later in a live client-side environment.