Search code examples
angularangular2-changedetection

Disable change detection propagation to parent


In my header component I show current time and update it every second. Since changing time does not influence any other component, I don't want Angular to run change detection every second for the whole graph of components. Each second, it seems that Angular runs change detection not only on parent component but also on components that are not on the path from header component to root component (sidenav and maincontent).

By having getter {{runChangeDetection}} in 3 other components' templates (sidenav, maincontent and mainview) I see in dev tools that every second these 7 rows are being printed:

  • CD HeaderComponent
  • CD MainContentComponent
  • CD SideTreeComponent
  • CD MAIN VIEW
  • CD MainContentComponent
  • CD SideTreeComponent
  • CD MAIN VIEW

I tried with onpush strategy with calling detectChanges() every second. Also with and without ChangeDetectorRef.detach().

What am I doing wrong? How to disable whole tree change detection run and path from the root to header component change detection.

header.ts:

  changeDetection: ChangeDetectionStrategy.OnPush
})
export class HeaderComponent implements OnInit {
  get runChangeDetection() {
    console.log('CD HeaderComponent');
    return true;
  }
  public time :Date = new Date()
  constructor(private cdr: ChangeDetectorRef){
    this.cdr.detach()
  }

  ngOnInit() {
    setInterval(() => {
      this.time = new Date()
      this.cdr.detectChanges()
    }, 1000)
  }
}

header.html:

<mat-toolbar>
  <div class="time">{{time | date: 'HH:mm:ss'}}</div>
</mat-toolbar>
{{runChangeDetection}} 

Solution

  • Found the answer. Re-running of change detection is not triggered by detectChanges(), but by setInterval method since this method is patched by Zone.

    You can check by deleting the line with detectChanges() and see that change detection re-runs even though no value is changed.

    Solution that worked for me is:

        this.ngZone.runOutsideAngular(() => {
          setInterval(() => {
            this.time = new Date()
            this.cdr.detectChanges()
          }, 1000)
        })