Expression has changed after it was checked — Angular Basics

Everything you need to know about the `ExpressionChangedAfterItHasBeenCheckedError` error2 min


Expression-has-changed-after-it-was-checked-—-Angular-Basics

When you are programming your project with angular, generally you can face the error like below,

Expression has changed after it was checked. Previous value: ‘ng-untouched: true’. Current value: ‘ng-untouched: false’

I will give an explanation and a solution to the issue.

Usually such questions (like, “Previous value : ‘foo foo’. Current value: ‘bar bar’”) come up because Angular developers do not understand how change detection works and why the check that produces this error is required. Many developers even view it as a bug. But it’s certainly not. This is a cautionary mechanism put in place to prevent inconsistencies between model data and UI so that erroneous or old data are not shown to a user on the page.


First, let me give you the solution, I know — Nobody reads theory much! 😉

You are listening to DOM element events.

(change)="onChange(element.value)"

And you are receiving an error relative to Angular forms.

Previous value: ‘ng-untouched: true’. Current value: ‘ng-untouched: false’

There are two conflicts!

Here, the ngModel is tracking changes to the form component.
Angular Forms are using(ngModelChange) to notify of component changes.
Angular Reactive Forms use valueChanges observables.

Solution: (Key Point)

In this case, You should be using (ngModelChange) instead of (change).

When you are using [(ngModel)] you don't have to use (ngModelChange) event.

Boring but interesting theory

Below explaination will give you brief idea about the underlying causes of the error and the mechanism by which it’s detected, provides a few common patterns that may lead to the error and suggests a few possible fixes.

After each operation, Angular remembers what values it used to perform an operation. They are stored in the oldValues property of the component view. After the checks have been done for all components Angular then starts the next digest cycle but instead of performing the operations listed above it compares the current values with the ones it remembers from the previous digest cycle:

  • check that values passed down to the child components are the same as the values that would be used to update properties of these components now
  • check that values used to update the DOM elements are the same as the values that would be used to update these elements now
  • perform the same checks for all child components

Please note that this additional check is only performed in the development mode. I’ll explain why in the last section of the article.

Let’s see an example. Suppose you have a parent component A and a child component B. The A component has a name and text properties. In its template it uses the expression that references name property:

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

And it also has B component in its template and passes the text property to this component through input property 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`;
    ...
}<>

So here is what happens when Angular runs change detection. It starts by checking A component. The first operation in the list is to update bindings so it evaluates text expression to A message for the child component and passes it down to the B component. It also stores this value on the view:

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

Then it calls the lifecycle hooks mentioned in the list.

Now, it performs the third operation and evaluates the expression {{name}}to the text I am A component. It updates the DOM with this value and puts the evaluated value to the oldValues:

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

Then Angular performs the next operation and runs the same check for the child B component. Once the B component is checked the current digest loop is finished.

If Angular is running in the development mode it then runs the second digest performing verification operations I listed above. Now imagine that somehow the property text was updated on the A component to the updated text after Angular passed the value A message for the child component to the B component and stored it. So it now runs the verification digest and the first operation is to check that the property text is not changed:

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

Yet it has and so Angular throws the error ExpressionChangedAfterItHasBeenCheckedError.

The same holds for the third operation. If the name property was updated after it was rendered in the DOM and stored we’ll get the same error:

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

You probably have a question in your mind now how is it possible that these values changed. Let’s see that.

Causes of values change

The culprit is always the child component or a directive. Let’s have a quick simple demonstration. I will use the simplest example possible but I will then show real-world scenarios after that. You probably know that child components and directives can inject their parent components. So let’s have our B component injects parent A component and update the bound property text.We will update the property in the ngOnInit lifecycle hook as it is triggered after the bindings have been processed which is shown here:

export class BComponent {
    @Input() text;    constructor(private parent: AppComponent) {}    ngOnInit() {
        this.parent.text = 'updated text';
    }
}<>

And as expected we get the 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 do the same for the property name that is used in the template expression of the parent A component:

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

And now everything works OK. How come?

Well if you take an attentive look at the order of the operations you will see that the ngOnInit lifecycle hook is triggered before the DOM update operation. That’s why there’s no error. We need a hook that is called after the DOM update operations and  ngAfterViewInit is a good candidate:

export class BComponent {
    @Input() text;    constructor(private parent: AppComponent) {}    ngAfterViewInit() {
        this.parent.name = 'updated name';
    }
}<>

And this time we get the expected 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’.

Of course, real-world examples are much more intricate and complex. The parent component properties update or operations that cause DOM rendering are usually done indirectly by using services or observables. But the root cause is always the same.

You may have that, Why it is running only during development mode?

I guess this is because an unstable model is not as dramatic a problem as a runtime error produced by the framework. After all, it may stabilize in the next digest run. However, it’s better to be notified of the possible error when developing an application than debug a running application on the client-side.

References:-

  • [1] StackOverflow
  • [2] In-depth dev
  • [3] Angular.io

I obtain an immense amount of satisfaction from helping others attain their goals and reach their potential through technology. Even if you wish to reach out and say "Hello", I sincerely appreciate all of the wonderful correspondence I receive each day from readers. You all enrich my life, and I hope I am able to do the same for you all as well.

If you find joy and value in what I do, please consider supporting my work with a donation — however much you can afford, it means and helps more than you can imagine.

You can give tips too using Buy me a coffee.

adsense


Discover more from 9Mood

Subscribe to get the latest posts sent to your email.


Like it? Share with your friends!

What's Your Reaction?

Lol Lol
0
Lol
WTF WTF
0
WTF
Cute Cute
0
Cute
Love Love
0
Love
Vomit Vomit
0
Vomit
Cry Cry
0
Cry
Wow Wow
0
Wow
Fail Fail
0
Fail
Angry Angry
0
Angry
Rakshit Shah

Legend

Hey Moodies, Kem chho ? - Majama? (Yeah, You guessed Right! I am from Gujarat, India) 25, Computer Engineer, Foodie, Gamer, Coder and may be a Traveller . > If I can’t, who else will? < You can reach out me by “Rakshitshah94” on 9MOodQuoraMediumGithubInstagramsnapchattwitter, Even you can also google it to see me. I am everywhere, But I am not God. Feel free to text me.

0 Comments

Leave a Reply

Choose A Format
Story
Formatted Text with Embeds and Visuals
List
The Classic Internet Listicles
Ranked List
Upvote or downvote to decide the best list item
Open List
Submit your own item and vote up for the best submission
Countdown
The Classic Internet Countdowns
Meme
Upload your own images to make custom memes
Poll
Voting to make decisions or determine opinions
Trivia quiz
Series of questions with right and wrong answers that intends to check knowledge
Personality quiz
Series of questions that intends to reveal something about the personality
is avocado good for breakfast? Sustainability Tips for Living Green Daily Photos Taken At Right Moment