5

How to configure Angular component not to trigger change detection for the whole application but only for the component itself and his children?

Working example: https://stackblitz.com/edit/angular-irc-starter-4txpbu?file=app%2Ftimer%2Ftimer.component.ts

There is a counter that increases value by one every second. It's TimerComponent and it's working as intended - value is increased and updated every second.

@Component({
  selector: 'app-timer',
  template: `{{value}}`
})
export class TimerComponent implements OnInit {
  value: number = 0;
  ngOnInit() {
    setInterval(() => this.value++, 1000);
  }
}

However, look at the parent component AppComponent.

<h3>Internal timer:</h3>
<p>Should not perform change detection for the whole app</p>
<app-timer></app-timer>

<h3>External binding</h3>
<p>Should not be updated after timer changes</p>
<p>{{someBindingProperty}}</p>

someBindingProperty is a getter:

get someBindingProperty() {
  this.bindingTimer++;
  return this.bindingTimer;
}

The problem: when TimerComponent increases value, change detection is triggered for the whole application. Is it possible to trigger change detection only within the component and children?

Note: if I add to TimerComponent:

changeDetection: ChangeDetectionStrategy.OnPush

Then changes are not detected within this component but change detection is still triggered for the whole application. So this is not a solution.

4
  • any particular reason you want to do so? neglecting parent dirty checking might cause your view in an inconsistent state... If you view state depends on dirty checking, it is a signal your code might be wrong
    – ABOS
    Commented Dec 19, 2018 at 17:54
  • this might work despite some errors from console (which I add a click button to demo it is still changeble) stackblitz.com/edit/angular-irc-starter-6fsui9
    – ABOS
    Commented Dec 19, 2018 at 18:10
  • 1
    @ABOS: To avoid overhead caused by countdown timer. If we only update internal state of the timer and there are no dependencies between timer state and outside app state, there is no need to update the whole app. However, when we want to emit some events outside, it's still possible by reentering Angular zone: stackblitz.com/edit/… Commented Dec 21, 2018 at 0:05
  • Unfortunately NgZone.run() reenables Angular zone for whole interval callback. Commented Dec 21, 2018 at 0:15

2 Answers 2

1

Another option to temporary disable change detection ChangeDetectorRef

enabled = true;  
constructor(private ref: ChangeDetectorRef)

toggleChangeDetection() {
  if (this.enabled) 
  {
    this.enabled = false;
    this.ref.detach();
  }
  else {
    this.enabled = true;
    this.ref.reattach();
}
2
1

You can run the interval for you timer outside of the angular zone and call ChangeDetectorRef.detectChanges everytime the timer value increases:

this.ngZone.runOutsideAngular(() => {
  setInterval(() => {
    this.timer++;
    this.cdr.detectChanges();
  }, 1000);
});

ChangeDetectorRef.detectChanges only detects changes for this view and its children.

I tried it out and using the Angular DevTools profiler you can see that neither the root component (AppComponent), nor the sibling component (SthWithDefaultCdComponent) got checked. Whether the timer component uses OnPush strategy or not does not make a difference.

enter image description here

Not the answer you're looking for? Browse other questions tagged or ask your own question.