Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I fix this specific NG0100: ExpressionChangedAfterItHasBeenCheckedError error?

Tags:

angular

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...

like image 619
markzzz Avatar asked Dec 09 '25 20:12

markzzz


1 Answers

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:

option 1: next macrotask (setTimeout)

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);
    });
  });
}
option 2: ChangeDetectorRef.detectChanges()

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:

option 3: fetching data in OnInit + data binding

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>

Extra - pending subscription

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:

  • just because it's a good practice, without wondering whether it's necessary or not. Today it may not be necessary, but tomorrow someone will modify the service and it may not be safe observable anymore;
  • If you unsubscibe in ngOnDestroy, the pending http request will be canceled, freeing browser pool of used requests
  • also no logic in the subscription and in the potential pipe'd transformations won't be called, so better performance due to not executing unnecessary code
  • and it will allow garbage collector to remove component instance faster
like image 95
carecki Avatar answered Dec 12 '25 08:12

carecki



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!