Search code examples
angularangular2-changedetection

Angular 2 change detector


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


Solution

  • 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