Does `changeDetection: ChangeDetectionStrategy.OnPush` in angular2 only works in one direction: top->bottom?
Asked Answered
H

2

3

Consider this plunker

import {Component, OnInit, Input, OnChanges, DoCheck, ChangeDetectionStrategy, EventEmitter} from 'angular2/core'

@Component({
  selector: 'child11',
  template: `
      <button (click)="change_obj()">Button in Child11</button>
      <div>This is child11: {{my_obj11['name']}}</div>
    `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class Child11 {
  @Input()
  my_obj11: Object;

  change_obj(){
    this.my_obj11['name'] = 'some other name';
  }  
}

@Component({
  selector: 'child1',
  template: `
      <child11 [my_obj11]="my_obj1"></child11>
      <div>This is child1: {{my_obj1['name']}}</div>
    `,
  changeDetection: ChangeDetectionStrategy.OnPush,
  directives: [Child11]
})
export class Child1 {
  @Input()
  my_obj1: Object;
}

@Component({
  selector: 'parent',
  template: `
    <div>
      <child1 [my_obj1]="my_obj" ></child1>
      This is my_obj in parent: {{my_obj['name']}}
    </div>
    `,
  directives: [Child1]
})
export class App {
  my_obj: Object = {'name': 'name1'};
}

Here is the relationship between components

 Parent
   |
 Child1
   |
 Child11

We note that Child1 have changeDetection: ChangeDetectionStrategy.OnPush

The above code is quite simple, parent send an object to child1 which sends the exact same object to child11.

child11 then update the primitive of the object.

We see that both parent and child1's object is updated even though child1 have changeDetection: ChangeDetectionStrategy.OnPush

I am guessing changeDetection: ChangeDetectionStrategy.OnPush only works one way: top to bottom.

Is that true?

If so is there a reason for this?

Hickie answered 23/4, 2016 at 19:55 Comment(0)
D
4

According to Savkin's blog post (well, it is buried in a comment to @vivainio),
with OnPush, Angular will only check the component for changes (i.e., check the template bindings) when

  • any of its input properties changes
  • it fires an event (e.g., a button click)
  • an observable fires an event [Note that this is not entirely correct. The observable needs to use | async in the view/template in order for change detection to run. See comments on this answer for more info.]

If any of those conditions are met, Angular will also "mark" all components up the tree to the root component as needing change detection. It will then run change detection. All of the ancestor components, even if they are configured for the OnPush strategy will be checked for changes, since they are currently "marked".

This explains why the views for the Parent/App and Child1 components get updated as a result of an event firing in component Child11.


This "mark up the tree" functionality is by design, and for the exact reason/scenario you show in your sample code – i.e., a component changes some application data, and ancestor components have data bindings for that same data in their views. How does Angular ensure the views will get updated? It has to "mark" all ancestors up to the root component so that when change detection runs, all of those components will be checked.

Note that this doesn't cover all scenarios though. Suppose that the changed application data is also present in a component view in some other, unrelated, "branch" of the component tree, and that branch uses OnPush. That component view will not get updated.

Demavend answered 25/4, 2016 at 16:2 Comment(0)
T
1

I know it has been a while, but i came across a similar problem. Hence i wanted to stress on the fact that OnPush does not always change the way parent and child component communication works. If the child component's @Input property is an object (reference type) then the only way OnChanges is triggered on the child is when the reference changes (new object is created) regardless of OnPush. OnPush makes more sense for template binding, requesting angular to do value equality or just compare references. Please do correct me if i was wrong.

Topo answered 20/7, 2016 at 9:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.