Debugging Expression has changed after it was checked

Posted By :Razat Vir Singh |30th December 2022

In this post, we will cover in detail an error communication that you'll sometimes come across while erecting Angular operations "Expression has changed after it was checked" 

 

ExpressionChangedAfterItHasBeenCheckedError.

 

We'll provide a thorough explanation for this error. We'll learn why it occurs, how to remedy it constantly, and how to fix it.

 

Most of all, we'll explain why this error is useful and how it ties back to the Angular Development Mode. In this post, we will cover the following

 

  • Understanding the" Expression has changed" error, why does it do?
  • The Angular Development mode
  • Debugging ways for changing the template expression that's causing the error
  • How to correct the "Expression has changed" error?
  • Conclusions

 

We'll first start by snappily remedying the error( a videotape is available for this), and also we will explain the cause and apply the fix. So without further ado, let's get started!

 

A common script where the error occurs

 

This type of error generally shows up beyond the original development stages, when we start to have some further expressions in our templates, and we've generally started to use some of the lifecycle hooks like AfterViewInit.

 

Then's a simple illustration of an element that's formerly throwing this error, taken from this former post

<div class="course">

    <div class="spinner-container" *ngIf="dataSource.loading$ | async">

        <mat-spinner></mat-spinner>

    </div>

    <mat-table class="lessons-table mat-elevation-z8" [dataSource]="dataSource">

        ....

    </mat-table>

    <mat-paginator [length]="course?.lessonsCount" [pageSize]="3"

                   [pageSizeOptions]="[3, 5, 10]"></mat-paginator>

</div>

 

This is a simple element that's displaying an Angular Material Data Table with a paginator, plus a leading index that gets displayed while we stay for the data to load.

 

Then's what this element looks like when the data is loaded

Material Data Table

And then's what the element looks like when the data is loading

Material Data Table

 

To find out why the" Expression has changed" error is being thrown in this situation, let's have a look at the simplified interpretation of this element

 

@Component({

    selector: 'course',

    templateUrl: './course.component.html'

})

export class CourseComponent implements AfterViewInit {

    @ViewChild(MatPaginator) paginator: MatPaginator;

       

    ngAfterViewInit() {

        this. paginator. page

            .pipe(

                start with(null),

                tap(() => this.dataSource.loadLessons(...))

            ).subscribe();

    }

}

 

As we can see, we're using ngAfterViewInit(), because we want to get a reference to the Paginator runner Observable, and the paginator is attained using the@ViewChild() view query.

 

Whenever the stoner hits the paginator navigation buttons, an event is going to be emitted that triggers the loading of a new data runner, by callingdataSource.loadLessons().

 

Note that the valve driver is the new pipeable interpretation of the RxJs do driver!

 

Because the runner Observable doesn't originally emit a value, we're emitting an original value using start with (). This causes the first runner of data to be loaded, else, data would only be loaded if the stoner clicks the paginator.

And then's the Data Source( simplified interpretation)

 

export class LessonsDataSource implements DataSource<Lesson> {

    private loadingSubject = new BehaviorSubject<boolean>(false);

    public loading$ = this.loadingSubject.observable();

    loadLessons(...) {

        this.loadingSubject.next(true);

  

        â€¦Load new data webpage from the backend.

    }    

}

 

As we can see, loadLessons() is emitting a new value for the lading$ Observable( it's setting the lading flag to true) and is doing so synchronously, before the asynchronous call to the backend.

 

Notice that this lading$ Observable is the bone that's getting used in the ngIf expression that shows or hides the leading index.

 

Error Communication illustration

 

The law over will throw the following change discovery error

 

ERROR: Error ExpressionChangedAfterItHasBeenCheckedError Expression has changed after it was checked, former value'( object Object)'. Current value' true'.

at viewDebugError(core.js9515)

at expressionChangedAfterItHasBeenCheckedError(core.js9493)

at checkBindingNoChanges(core.js9662)

 

This error tells us that there's a problem with an expression in the template, but the question is, which expression? And why does it beget an error?

 

Remedying "Expression has changed after it was checked"

 

The debugging process that we will go over below is also done then step by step in this videotape, where we also explain the cause of the error

 

Then's how we can identify the problematic expression. In the Chrome Dev Tools press, we have a call mound that identifies where exactly the error passed.

 

Let's click on the link available in the first line of the call mound

at viewDebugError(core.js9515)

 

This will open the DevTools Javascript Debugger in the line where the error passed let's also add a homemade breakpoint on that line. Still, the breakpoint will hit and we will get the following

If we now reload the element and spark the error again.

 

Debugging ExpressionChangedAfterItHasBeenCheckedError

 

The program is now firmed at this point, and we can hang over the variables and go up and down the call mound, to see what's going on.

 

Notice that line 9515 that's where the error occurs, and the line number with the blue triangle is where we clicked to produce the breakpoint.

 

We also have a call stack. However, we will see the function call to viewDebugError, If we start clicking on the functions up the call mound.

 

Relating the former value of the Expression

By pressing the oldValue variable, we can see that the old value of the problematic expression was false, and according to the communication it's now true

 

Debugging ExpressionChangedAfterItHasBeenCheckedError

Relating the Problematic Expression

 

But what template expression is causing this error? If we keep clicking up the call mound, we're going to see that a template expression will appear

 

Debugging ExpressionChangedAfterItHasBeenCheckedError

 

As we can see, this is the ngIf expression that shows or hides the leading index so this is the problematic template expression!

 

As we can see, the source maps generated by the Angular CLI are veritably useful to troubleshoot this kind of problem.

 

The understanding of  "Expression has changed after it was checked" Error

 

This ngIf expression, at first sight, doesn't feel problematic. So why does this throw an error? Then what happens is the ngIf expression over is originally false because the data source isn't loading any data, so loading$ emits false. Still, also the lading index should be hidden as no data is being loaded

If the lading$ Observable last emitted value is false. while Angular is preparing to modernize the View grounded on the rearmost data changes, during that process it calls ngAfterViewInit, which triggers the loading of the first data runner from the backend.

 

Loading the data would still take a while and it's an asynchronous operation, so the data won't arrive incontinently

 

Then's the problem as a coetaneous call to dataSource. loadLessons() is made, a new true value of the lading$ flag is emitted incontinently

 

And it's this new value of the lading flag that accidentally triggers the error!

 

Let's learn why streamlining this flag during the view construction process is problematic.

 

The" View Updates Itself" script

 

The problem then's that we have a situation where the view generation process( which ngAfterViewInit is a part of) is itself further modifying the data that we're trying to display in the first place.

 

The lading flag starts with false, we tried to display that on the screen, by hiding the leading index. The way that ngAfterViewInit is written causes the presentation of the data to further modify it. After the view is erected, the lading flag is now true. So which value is the lading flag also, true or false? Due to the lack of a decision-making mechanism, Angular preventively throws this error, which only occurs in Development Mode.

 

Check out this post to learn more about the Angular Development Mode. Let's now look at a solution to this problem as well.

 

Understanding the result

 

So then's the result we can not use the paginator. page reference in ngAfterViewInit() and incontinently call the Data Source because that will spark a further revision of the data before Angular could display it on the screen, so it's not clear if the value of the lading flag should be true or false.

 

To break this issue, we need to let Angular first display the data with the lading flag set to false.

 

Also, in some unborn time, in a separate Javascript turn, only are we going to call the Data Source loadLessons() system, which will beget the lading flag to be set to true and the lading index will also get displayed.

 

Original perpetration of the result

 

To postpone the law inside ngAfterViewInit to another Javascript turn then's one original perpetration that will help us to understand the result more

 

ngAfterViewInit() {

    setTimeout(() => {

        this. paginator. page

            .pipe(

                start with(null),

                tap(() => this.dataSource.loadLessons(...))

            ).subscribe();

    });

}

 

This formerly solves the problem we do not have an error presently!

 

As we can see, we're using setTimeout() to postpone this law to another Javascript Virtual Machine turn, and notice that we aren't indeed specifying a value for the downtime.

 

Let's now have a look at an indispensable perpetration with lower law nesting, and also we will explain why this fixes the issue.

 

A volition using RxJs

 

This is an indispensable interpretation that looks better due to lower law nesting and uses the RxJs pipeable driver detention

 

import { start with, tap, delay } from 'rxjs/operators;

ngAfterViewInit() {

  this.paginator.page

      .pipe(

          startWith(null),

          delay(0),

          tap(() => this.dataSource.loadLessons(...))

      ).subscribe();

}

 

How does setTimeout or detention( 0) fix this problem?

 

Then's why the law above fixes the issue

 

The original value of the flag is false, so the leading index will NOT be displayed originally

 

ngAfterViewInit() gets called, but the data source isn't incontinently called, so no variations of the lading index will be made synchronously via ngAfterViewInit()

 

Angular also finishes rendering the view and reflects the rearmost data changes on the screen, and the Javascript VM turn completes

 

One moment latterly, the setTimeout() call( also used inside detention( 0)) is touched off, and only also the data source loads its data

 

The lading flag is set to true, and the lading index will now be displayed

 

Angular homestretches render the view and reflect the rearmost changes on the screen, which causes the lading index to get displayed. This eliminates the error, which fixes the error communication.

 

Moving the initialization of the data to ngOnInit()

 

But in this case, an indeed better result exists! The core of the problem is that we're modifying the data being displayed( the lading flag) inside ngAfterViewInit().

 

So let's remove the call to startWith( null) that loads the original runner, and rather, lets detector the lading of the original data in ngOnInit()

 

@Component({

    selector: 'course',

    templateUrl: './course.component.html'

})

export class CourseComponent implements AfterViewInit, OnInit {

    @ViewChild(MatPaginator) paginator: MatPaginator;

       

    ngOnInit() {

      // load the initial page

      this.dataSource.loadLessons(...);

    }

    ngAfterViewInit() {

        this.paginator.page

            .pipe(

                tap(() => this.dataSource.loadLessons(...))

            ).subscribe();

    }

}

 

This also solves the issue, with this we do not have the error presently.

With this new interpretation, there's no revision of the template data in the ngAfterViewInit() lifecycle hook, so the problem doesn't do.

Let's now wrap effects up by talking about what would be if this error would NOT be thrown.

 

Conclusions

In summary, Angular protects us from erecting programs that are hard to maintain in the long- term and reason about, by throwing the error" Expression has changed after it was checked"( only in development mode).

 

Although a bit surprising at first sight, this error is constructive!

Why" Expression has changed after it was checked" is useful

 

What would be if the view generation process could modify the rendered data? This could be veritably problematic, to start we could indeed produce a horizonless circle!

 

Further generally, then what would imagine having a UI that behaves in an erratic way, where occasionally the stoner can not see the data in our element, and aimlessly sees some former interpretation of the data.

 

Also, the stoner clicks or hovers some unconnected UI rudiments which are to spark an event tutor, and now another unconnected factor is affected. This kind of error can be veritably hard to reproduce, troubleshoot, and reason about.

 

One of the main pretensions of using a web frame like Angular is the guarantee that the data in our factors will always get reflected rightly in the view, and that we do not have to do that synchronization ourselves.

 


About Author

Razat Vir Singh

Razatvir Singh is an experienced MEAN stack developer with a diverse skill set and practical experience in using the latest tools and frameworks. His expertise includes Angular, Node.js, HTML, CSS, JavaScript, jQuery, and databases such as MongoDB and Opensearch. He has worked on various projects, such as E-Cart, a fully functional e-commerce website, MS-Excel Clone Project, a project where data is fetched from a popular sports website called "CricInfo.com" using Node.js packages and JavaScript, Konfer Project and he has also worked on other projects like Automation and Prediction of Stock Prices using Machine Learning. With his diverse skill set and expertise in the latest technologies, Razatvir is well-equipped to take on any MEAN stack development project and deliver high-quality solutions that meet the client's requirements.

Request For Proposal

[contact-form-7 404 "Not Found"]

Ready to innovate ? Let's get in touch

Chat With Us