JoyfulPanda
JoyfulPanda

Reputation: 1047

setTimeOut() gets repeated in Angular Lifecycle Hooks: ngDoCheck, ngAfterContentChecked, ngAfterViewChecked

Reproducibility

I have a small TypeScript code snippet as below:

ngAfterViewChecked(){
    console.log("ngAfterViewChecked 1");
    setTimeout(() => {
      console.log("ngAfterViewChecked 2");
    }, 1000);
  }

Function setTimeOut() is supposed to get the lambda function called after 1 second and then stop. However, the hook ngAfterViewChecked() is called continuously (excerpt from Chrome Developer Tool Console):

00:36:50.827 home.component.ts:53 ngAfterViewChecked 1
00:36:51.842 home.component.ts:55 ngAfterViewChecked 2
00:36:51.843 home.component.ts:53 ngAfterViewChecked 1
00:36:52.843 home.component.ts:55 ngAfterViewChecked 2
00:36:52.844 home.component.ts:53 ngAfterViewChecked 1
00:36:53.845 home.component.ts:55 ngAfterViewChecked 2
00:36:53.846 home.component.ts:53 ngAfterViewChecked 1
00:36:54.848 home.component.ts:55 ngAfterViewChecked 2
...

Without the setTimeOut(), function ngAfterViewChecked() is called once. This issue occurs also with ngDoCheck(), ngAfterContentChecked().

Test with other Angular Lifecycle Hooks

The same code body, with or without setTimeOut(), declared in constructor(), ngOnInit(), ngAfterContentInit(), ngAfterViewInit(), gets called once as expected.

Environment

My guess

The hooks constructor(), ngOnInit(), ngAfterContentInit(), ngAfterViewInit() are called once in the entire Lifecycle of a Component, regardless of how its content evolves through out its life. So this issue reasonably doesn't happen in these hooks.

With ngDoCheck(), ngAfterContentChecked() and ngAfterViewChecked(), they get called when Angular detects changes. But as showed in the lambda function body, only simple console.log() is used. I think Angular might intercept the setTimeOut() call and blindly believes that there could have been some changes, so it starts the Change Detection process, which leads to what we have seen: calls chained infinitely.

Question

Is this a bug or a feature?

Upvotes: 6

Views: 2351

Answers (1)

matmo
matmo

Reputation: 1379

Feature - Angular's change detection runs after asynchronous operations complete (like setTimeout) - so the setTimeout itself it causing ngAfterViewChecked to run, and vice versa.

You can run things outside of Angular's Zone if you don't want that to happen, i.e.

   ngAfterViewChecked(){
    console.log("ngAfterViewChecked 1");

    this.ngZone.runOutsideAngular(() => {
      setTimeout(() => {
        console.log("ngAfterViewChecked 2");
      }, 1000);
    });
  }

You can inject NgZone in your constructor, i.e. private ngZone: NgZone. Check out the relationship between Angular and zone.js for more info.

Upvotes: 11

Related Questions