Reputation: 4302
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
Upvotes: 1
Views: 9213
Reputation: 449
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)});
};
Upvotes: 0
Reputation: 2199
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();
});
}
}
Upvotes: 3
Reputation: 1263
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.
Upvotes: 1