avatarHeloise Bahadiroglu

Summary

The Angular "Expression Changed After It Has Been Checked" error occurs when a child component changes a property of a parent component within the same change detection cycle, violating Angular's top-to-down data flow logic.

Abstract

The "Expression Changed After It Has Been Checked" error in Angular is a common yet perplexing issue that arises when a child component alters a property of its parent component too early in the change detection cycle. This error, which only manifests in development environments, indicates a disruption in Angular's unidirectional data flow, where changes should flow from parent to child components. The issue stems from Angular's verification process, which compares the initial values of properties with their values after the change detection cycle. If a discrepancy is detected, the error is thrown. A common workaround involves using setTimeout to defer the change until after Angular's cycle has completed. While the error can be ignored in production environments, it serves as a warning that the application's architecture may lead to unpredictable behavior and should be addressed by adhering to Angular's recommended data flow practices.

Opinions

  • The setTimeout solution, while often criticized as a hack, is considered a valid approach to defer changes and avoid the error.
  • Ignoring the error in production is not recommended, as it can lead to underlying issues that affect the stability of the application.
  • The error is seen as a design flaw in an Angular application, suggesting that developers should rethink how data is passed between components to maintain a clear and predictable data flow.
  • The article suggests that understanding the Angular change detection mechanism is crucial for diagnosing and resolving this error, implying that a deeper knowledge of Angular's inner workings is beneficial for developers.
  • The error is described as a potential interview question, indicating its relevance as a topic that tests a developer's understanding of Angular's change detection strategy.

Angular: Understand the ‘Expression Changed After It Has Been Checked’ Error

Photo by Elisa Ventur on Unsplash

This might be the most confusing Angular error. It doesn’t quite tell you where the issue, nor how to fix it. And it only appears in development environments. If you google it, proposed solutions are usually to use a setTimeout somewhere, and that would actually work. And even though it looks like an ugly workaround, it might actually be a good solution.

But where is this issue coming from exactly? How do we fix it / why is a setTimeout a solution? As it is only in development environments, can’t we just ignore it?

Understanding this error requires some understanding of Angular change detection. That can be a pretty tricky interview question. The basic idea behind this error is that a child component is changing a property of a parent component. This is not exactly forbidden, but Angular has a top-to-down logic: parents should be setting values in children, not this other way around. If a child changes a value in a parent component, too early, that is to say within one change detection cycle, Angular will throw this error.

Let’s give an example with a child component:

import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit {
  @Input() name!: string;
}
{{name}}

and a parent component:

import { Component } from '@angular/core';

@Component({
  selector: 'app-parent',
  templateUrl: './parent.component.html',
  styleUrls: ['./parent.component.scss']
})
export class ParentComponent {
  name = 'Parent name';
}
<app-child [name]="name"></app-child>

This is completely fine and doesn’t cause any error. If the child component changes this name property on init though, the ExpressionChangedAfterItHasBeenCheckedError appears:

import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit {
  @Input() name!: string;
  @Output() nameChanged = new EventEmitter<string>();

  ngOnInit(): void {
    this.nameChanged.emit('Child name');
  }
}
<app-child [name]="name" (nameChanged)="name = $event"></app-child>

Where does that come from exactly?

Without going into too much details, we need to talk a bit about the Angular change detection. The ultimate aim for Angular is to generate the DOM depending on your components and their properties. When you pass a property down from a parent to a child component, Angular goes through different steps. In our case, for a first rendering, Angular will:

  1. set the value of name in the child component (the name variable, not yet what is displayed in the DOM)
  2. call ngOnInit, ngDoCheck on the child component
  3. update the DOM
  4. call ngAfterViewInit on the child component

In the development environment, and in the development environment only, Angular will then add a verification step after the fourth step. It keeps the values it used in the step 1 to step 4 and compares them to the values it has at the end. And if it isn’t the same, it throws an error.

To come back to our example, in the step 1 Angular keeps the value “Parent name” as oldValue. Then in the step 2, the ngOnInit of the child component is called, so the name value in the parent becomes “Child name”. At the end of the cycle, Angular compares oldValue and the new name value, they aren’t the same, an error is thrown.

So, to say it simply, the issue is happening because we change the value of name too early. A solution, if we really have to change the value in the child component, is therefore to change it later. That’s why wrapping the code in setTimeout works: you are changing the value of the name way after Angular is done, in a new VM turn.

import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';

@Component({
  selector: 'app-child',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit {
  @Input() name!: string;
  @Output() nameChanged = new EventEmitter<string>();

  ngOnInit(): void {
    setTimeout(() => {
      this.nameChanged.emit('Child name');
    });
  }
}

You are probably not directly changing a parent property in your child component’s initialisation though. So why do you get this error?

While your case might not be as straight forward, the idea has to be the same, you must be changing the value of some attribute from the child up into the parent. It is most likely happening in a service (and your child and parent component are sharing a same value from it) or in a directive.

As this error only appears in development, you might also be tempted to just ignore it. It does only happen in development because Angular doesn’t perform this extra check in other environments. It doesn’t mean that can’t cause issues. Values are meant to be passed down in Angular. This error is telling you that you are changing a value “up”. This can’t be good.

In Plain English

Thank you for being a part of our community! Before you go:

Angular
Programming
JavaScript
Coding
Troubleshooting
Recommended from ReadMedium