I've read about NG0100: ExpressionChangedAfterItHasBeenCheckedError in Angular, but I can't avoid it in my case.
Basically, on the interceptor, I've a service that load a "status" true/false:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<any> {
this.showLoader();
return next.handle(req).pipe(
catchError((error) => {
return error;
}),
finalize(() => {
this.hideLoader();
})
);
}
Using ngAfterViewInit within app component introduce that error:
ngOnInit(): void {}
ngAfterViewInit() {
this.getData().subscribe((data) => {
this.childSelector.loadRecipeRoadmap(data.name);
});
}
And I need to use it: in fact, when all child are loaded, parent must "send" (once) data to child (only at the beginning). And at some point, I just need to read from child (that's why I use ViewChild and not @Output mechanism).
How can I fix this specific error? Should I sync Observable? Not sure how...
It's because you are executing some code in the ngAfterViewInit, which modifies the data that is displayed.
First detection cycle evaluated the ngIf to false, then ngAfterViewInit was executed, then after it the second verifying detection cycle (angular development mode has this additional one) was executed and this time ngIf was evaluated to true. Hence the infamous error.
There are a few solutions to this:
Since your data fetching is asynchronous anyway, you can postpone it to be called in next macrotask (after ngAfterViewInit is finished) with a help of setTimeout with 0 time delay:
ngAfterViewInit() {
setTimeout(() => {
this.getData().subscribe((data) => {
this.childSelector.loadRecipeRoadmap(data.name);
});
});
}
You may inform angular to run additional detection cycle after you modified the data that is displayed.
constructor(private http: HttpClient, public loader: LoaderService, private changeDetectorRef: ChangeDetectorRef) {}
ngAfterViewInit() {
this.getData().subscribe((data) => {
this.childSelector.loadRecipeRoadmap(data.name);
});
this.changeDetectorRef.detectChanges();
}
Or maybe more angular-ish way:
Instead of waiting for child to be created and interact with it directly, execute data loading in OnInit and bind the data to the child component:
export class AppComponent {
dataForChild: any;
constructor(private http: HttpClient, public loader: LoaderService) {}
ngOnInit(): void {
this.getData().subscribe((data) => this.dataForChild = data.name);
}
And bind it with (of course myData in child component need to become and input property with @Input())
<app-my-child [myData]="dataForChild"></app-my-child>
You are subscribing to the observable, but do not unsubscribe from it (neither with unsubribe() nor using takeUntil()). Although subscriptions to observables from angular http module doesn't cause memory leaks, it's good to make sure you will unsubscribe from it anyway for a few reasons:
ngOnDestroy, the pending http request will be canceled, freeing browser pool of used requestspipe'd transformations won't be called, so better performance due to not executing unnecessary codeIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With