1

Though I know the reason as to why this ERROR happens, However, I would like to know how to overcome in the following scenario:

I have a generic loader in app.component. For some of the child routes it'll update the loader value on ngOnInit life cycle until records are fetched. Also, I do not want to use resolver as I'm parsing the query params in the component and then passing to the service to fetch the records.

The ERROR is due to the default value of the loader to false and changes to true during the child component life cycle ngOnInit hook.

Please have a look at the following snippet:

// app.component.ts
 viewModel = {
    showLoader: false
};
ngOnInit() {
    this.eventService.on(ApplicationEvents.SHOW_FULL_PAGE_LOADER, (value) => {
        this.viewModel.showLoader = value;
    });
}
// app.component.html
<div class="full-page-loader" *ngIf="viewModel.showLoader"><em class="fa fa-cog fa-spin loader-icon"></em></div>

And following is the snippet from the lazyloaded child component:

ngOnInit() {
    this.loadData();
    this.cart = this.cartService.getCart();
  }

private loadData() {
this.eventService.showFullPageLoader(); // <-- This will emit an event to show the loader 
this.umrahDataService.getServices(UmrahServiceType.Visa).then((resp) => {
  this.visas = resp.map((v) => {
    v.image = '/assets/img/umrah/visa-processing.jpg';
    return v;
  });

  this.eventService.hideFullPageLoader();
  this.actionInProcess = false;
}).catch((er) => {
  this.alertService.alert('SERVER_ERROR');
  this.eventService.hideFullPageLoader();
  this.actionInProcess = false;
});
}

ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ngIf: false'. Current value: 'ngIf: true'. at viewDebugError (core.js:19629) at expressionChangedAfterItHasBeenCheckedError (core.js:19617) at checkBindingNoChanges (core.js:19833) at checkNoChangesNodeInline (core.js:29533) at checkNoChangesNode (core.js:29522) at debugCheckNoChangesNode (core.js:30126) at debugCheckDirectivesFn (core.js:30054) at Object.eval [as updateDirectives] (AppComponent.html:3) at Object.debugUpdateDirectives [as updateDirectives] (core.js:30043) at checkNoChangesView (core.js:29421)

Although I tried BehaviorSubject with async pipe as well and that didn't help either. Looking forward for the feedbacks. Thanks

Basheer Kharoti
  • 4,202
  • 5
  • 24
  • 50

3 Answers3

1

In most cases moving your reactive code to ngAfterViewInit helps. So in your child component instead of:

ngOnInit() {
    this.loadData();
    this.cart = this.cartService.getCart();
}

use

ngAfterViewInit() {
    this.loadData();
    this.cart = this.cartService.getCart();
}

You could also use setTimeout in your app.component.ts, but that is more like a hack rather than a solution.

Yuriy Kravets
  • 1,183
  • 2
  • 13
  • 28
1

Whenever you change data after view has been loaded, let Angular watcher to digest the changes. Angular has ChangeDetectorRef service. You can inject this service and force the angular to detect and include the changes in the digest cycle.

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

@Component({
  selector: 'app-your-comp',
  templateUrl: 'your-comp.component.html',
  styleUrls: ['your-comp.component.scss']
})
export class YourCompPage {
    constructor(private changeDetectorRef: ChangeDetectorRef) {}

    ngAfterViewInit() {
        ..any data changes or your flag in this case..
        this.changeDetectorRef.detectChanges();
        // -- OR --
        this.eventSubscription.someEvent(() => {
            ..any data changes or your flag in this case..
            this.changeDetectorRef.detectChanges();
        });
    }
}

P.S. https://indepth.dev/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error/

Ashokan Sivapragasam
  • 2,033
  • 2
  • 18
  • 39
  • Thanks for your answer, however, I did try running `CD` manually as well. `this.cd.detectChanges();` but that didn't work. – Basheer Kharoti Apr 01 '20 at 10:46
  • According to this article and Yuriy, it is better to do it in ngAfterViewInit() and change-detector for digesting. https://indepth.dev/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error/ – Ashokan Sivapragasam Apr 01 '20 at 10:51
  • Nope. `ngAfterView` is throwing the same error however, I've added a hidden class but rather to remove the div from the DOM. Though I am still looking forward for the actual solution. – Basheer Kharoti Apr 01 '20 at 12:58
0

Angular change detection Process is Synchronous, to avoid this error we can make this as async process, and we can make that code to run outside of Change Detection by doing something like.

ngOnInit(){
  this.ngZone.runOutsideAngular(() => {    
     this.interval = window.setInterval(() => {
      this.eventService.on(ApplicationEvents.SHOW_FULL_PAGE_LOADER, (value) => {
      this.viewModel.showLoader = value;
      });
  }, 1)});
};