Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

  • The program is very simple (like "Hello World" for Angular). No 3rd party library.
  • Both Angular 5 & 6 have this issue (lower versions haven't been tested). They were installed separately in different machines.

    Angular CLI: 6.0.8
    Node: 8.11.3
    OS: win32 x64
    Angular: 6.0.9
    
    
    Angular CLI: 1.7.4
    Node: 8.11.1
    OS: win32 x64
    Angular: 5.2.10
    
  • Windows 8.1 x64.

  • Google Chrome 67 (64 bit).

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?

like image 727
JoyfulPanda Avatar asked Jan 28 '23 18:01

JoyfulPanda


1 Answers

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.

like image 182
matmo Avatar answered Feb 15 '23 09:02

matmo