testing
testing

Reputation: 2343

Why is both reference change and primitive change catch by Angular2 during change detection even with OnPush flag set?

Consider the following code

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

@Component({
  selector: 'child1',
  template: `
    <div>reference change for entire object: {{my_obj1.name}}</div>
    <div>reassign primitive in property of object: {{my_obj2.name}}</div>
    <div>update primitive in property of object: {{my_obj2.num}}</div>
    `,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class Child1 {

  @Input()
  my_obj1: Object = {'name': ''};

  @Input()
  my_obj2: Object = {'name': '', 'num': 0};

  ngDoCheck() {
    console.log('check from child1');
    console.log(this.my_obj1);
    console.log(this.my_obj2);
  }
}

@Component({
  selector: 'parent',
  template: `
    <div>
      <child1
        [my_obj1]="my_obj1"
        [my_obj2]="my_obj2"
      >
      </child1>
      <button (click)="change_obj1()">
        Change obj1
      </button>

    </div>
    `,
  directives: [Child1]
})
export class App {

  my_obj1: Object = {'name': 'name1'};
  my_obj2: Object = {'name': 'name2', 'num': 0};

  change_obj1() {
    this.my_obj1 = {'name': 'change1'}
    this.my_obj2['name'] = 'change2';
    this.my_obj2['num'] += 1;
  }
}

From the experiment I made, here is my understanding of the current Angular2 change detection strategy, can someone verify it if its true?

  1. Angular2 by default checks for value equality when doing change detection. If there are no ChangeDetectionStrategy.OnPush, every watched variable in component tree is checked for value equality. If value equality is false, that specific component will be rerender, and if value equality if true, that specific component will not be rerender.

  2. If you add ChangeDetectionStrategy.OnPush to a component. The behavior changes as follows

    i. If variable inside the component have reference change, the component is rerendered, and child component is checked for change detection (its specific change detection algorithm value/reference check depends on ChangeDetectionStrategy.OnPush)

    ii. If variable inside the component don't have reference change, the component is not rerendered, and child component is not checked for change detection, regardless of presence of ChangeDetectionStrategy.OnPush

Is this the correct interpretation?

Upvotes: 2

Views: 2637

Answers (2)

Mark Rajcok
Mark Rajcok

Reputation: 364677

I reworked your plunker a bit: new plunker

Since primitive values are immutable, there is no difference between reassigning and updating – the primitive gets the new immutable value, so I removed the "updating" code. Also, it is very helpful to split out assigning a new object reference (which does trigger change detection) and assigning a new primitive value (which does not trigger change detection). So I did that also.

If you run my Plunker we can make the following observations:

  1. changing an input property that is a reference type on an OnPush component will update the component's view. The template bindings are checked for changes. In addition, child components are checked (assuming they are not using OnPush).
  2. changing a primitive property that is contained within a reference type on an OnPush component will not update the component's view. The template bindings are not checked for changes. In addition, child components are not checked, regardless of whether they are using OnPush or not.
  3. ngDoCheck() is always called on the first OnPush component, regardless of whether the template bindings are checked for change or not. I find this odd (who knows, maybe it is a bug). So, just because ngDoCheck() is called does not necessarily imply that template bindings are checked.

Note, when a template binding change is detected, only that change is propagated to a child component or the DOM (as appropriate, depending on the binding type). If the binding change resulted in a DOM change, the entire component is not rerendered. Only that bound DOM data is updated, and the browser will only then update that one DOM element. (This is unlike some other frameworks, where they do rerender the entire template if any change is found. This helps make Angular faster.)

Upvotes: 4

nikk wong
nikk wong

Reputation: 8670

This post explains it in detail pretty well:

http://victorsavkin.com/post/133936129316/angular-immutability-and-encapsulation

In short you're assumptions are correct. Angular2 must be conservative and check for value equality, i.e. it must do a 'deep check' of the objects referenced.

With ChangeDetectionStrategy.OnPush, components will only be updated if the references to their input objects have changed.

Thus is why immutable objects can be preferred data structures--if we must update an object, the component is now referencing a new object. And it is therefore easy for angular to know which components must be updated.

Performant behavior can also be achieved with observables, through the ChangeDetectorRef.markForCheck(); method.

This is explained here:

http://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html

Upvotes: 0

Related Questions