'Why does Angular trigger ChangeDetection on parent components on DOM events despite OnPush strategy?

trick question I haven't found any answer in the documentation yet.

OnPush strategy is known to trigger ChangeDetection in the following cases :

  1. when a DOM event the component listens to was received
  2. when the |async pipe receives a new event
  3. when an @Input()
  4. when ChangeDetectorRef::markForCheck is explicitly called (or any other similar methods like, ApplicationRef::tick, ChangeDetectorRef::detectChanges

So when a DOM event is triggered on a component, it won't affect CD on a sibling component. BUT it will be triggered on every parent component in his hierarchy wether Default or OnPush. This does not happend when calling detectChanges for example where only the component gets CD.

I also noted, ApplicationRef.tick() will only trigger CD on non-OnPush components.

Why doesn't OnPush provide total isolation and still trigger CD on parents ?

Please see a working illustration on stackblitz



Solution 1:[1]

Maybe you have the same misunderstanding about OnPush that I had when I started learning about change detection :). The point is: the OnPush strategy does not change anything about which events trigger a CD cycle. This is solely taken care of by zone.js by patching browser APIs. (Unless CD is triggered manually.) zone.js does not care about components and ChangeDetectionStrategies. And CD always starts at the root as @Chris Hamilton explained.

The difference in ChangeDetectionStrategy.Default and OnPush is only if the component will actually be CHECKED during a CD cycle.

So the sentence in your original post should rather be: "OnPush strategy is known to only mark a component to be checked in the following cases".

See below an example that I observed with the profiler of the Angular DevTools. I put a setTimeout(() => {}, 6000) in the ngOnInit of the OnPush component, it triggered a CD cycle (the source is "setTimeout"), but it did not cause the component itself to be checked.

enter image description here

About the differences between manually triggered detectChanges(), tick() and markForCheck():

  • ChangeDetectorReference.detectChanges() triggers CD on this component and its children by respecting the CD strategy of the children
  • ApplicationRef.tick() triggers CD for the whole application by respecting CD strategies
  • ChangeDetectorReference.markForCheck() does NOT trigger CD, but marks all OnPush parents to be checked once in the current or next CD cycle

(well explained here)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1