3

I´m trying to understand the change detector mechanism in Angular 2 applications. I think I made some progress reading several posts :) but not a lot. For what I understand the situation is like this:

When the component is not using the OnPush strategy then it will run the associated change detector in case of:

1) Any part of the app launch a DOM event 2) Any part of the app resolve an HTTP request 3) Any parte execute async code with setTimeout or setInterval

All the components will be checked top to bottom in the component tree.

When the component is using the OnPush strategy then it will run the change detector when:

1) Any of its @Input change its reference (Immutability involved here) 2) The component (or a child) launch an event 3) An Observable launch an event. In this case, if we use Observable as @Input then we have to call the markForCheck() method in the change detector.

OnPush components mark all its subtree of components for not running change detection as well.

The change detection always start at the root component and will go top to bottom the component tree.

So I build a sample application (links at the end of the text) with a tree of components in 3 levels (parent, child and grand-child). The second level components are OnPush components. All the components have click events attached so when click any of them the change detector is executed. When the ngAfterViewChecked hook is executed I change the component color to red for some seconds.

What I can´t understand is why when I click on grand-child components all the child change detectors are running. I thought only the one clicked in the subtree from parent to grand-child will be executed.

When I click the parent is the same. All childs detectors are executed. Why??

Any good person can explain me what is going on?

Thanks!!

Github project

Github pages deployed project

yagopv
  • 317
  • 4
  • 9
  • Here is the link to a video on YouTube. This is the best one I have come across so far to understand ChangeDetection in Angular2 https://youtu.be/CUxD91DWkGM – Santanu Biswas Jan 31 '17 at 20:10
  • Thanks, I saw it a few days ago before starting with the experiment app :) – yagopv Feb 01 '17 at 09:22

1 Answers1

4

I feel your understanding of change detection is on point. There might be 2 misunderstandings I would like to underline that might help you solve this puzzle.

1- ngAfterViewChecked is unrelated to whether a change was detected, the documentation[1] says "Notice that Angular frequently calls AfterViewChecked, often when there are no changes of interest. Write lean hook methods to avoid performance problems." after an example.

You might want to use ngOnChanges()[2] instead. The documentation says there that "Angular only calls the hook when the value of the input property changes"

2- When changing the state of a component from within a component (setTimeout, rx subscription fired, ...) you will want to inject ChangeDetectorRef and call ChangeDetectorRef.markForCheck()[3].

This is because OnPush only considers changed @Input. ngOnChanges() won't be triggered because no input will be changed.

[1] https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#aftercontent

[2] https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#onchanges

[3] https://angular.io/docs/ts/latest/api/core/index/ChangeDetectorRef-class.html

Ben Dadsetan
  • 1,565
  • 11
  • 19
  • Hi, Thanks for the answer!!. Then ... I think there is no reliable way to know when the change detector is fired because the ngOnChanges() method only gets called when @Input() changes happened but with event firing from the component is not the case – yagopv Feb 01 '17 at 09:26
  • I have played around with this a little, but basically it looks like when you are having updates outside of the angular framework, you can mark components as changed with ChangeDetectorRef.markForCheck() but indeed need to manage separately any that go else where. A common patter would however be to use @Output to communicate upward to parents rather than rely on the angular framework change detection itself anyway. Might want to try that? – Ben Dadsetan Feb 24 '17 at 20:25