Search code examples
javascriptangulartypescriptangular2-changedetection

Why does attribute change still cause the @Input to be change in ngDoCheck when change detection is on push?


Consider this plunker

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  selector: 'my-app-n1',
  template: `
    <my-app-n2 [from2]="from1"></my-app-n2>
    <div>
      {{from1.name}}
      <input type="button" value="n1"  (click)="changen1()">
    </div>
  `,
})
export class App2 {

  @Input() from1;

  ngDoCheck() {
    console.log('do check 1 ', this.from1);
  }

  ngOnChanges(changes: any) {
    console.log('n1 change ', changes);
  }  
  changen1() {
    this.from1 = {'name': 'name1-2'}
  }
}

@Component({
  selector: 'my-app',
  template: `
    <my-app-n1 [from1]="from"></my-app-n1>
    <input type="button" value="root" (click)="changen0()">
  `,
})
export class App {
  from: any;
  constructor() {
    this.from = {'name': 'name0'}
  }

  ngOnChanges(changes: any) {
    console.log('n0 change ', changes);
  }  

  changen0() {
    this.from.name = 'name-0-2-2'
  }

}

Notice that App2 uses ChangeDetectionStrategy.OnPush

When changen0 is clicked I expect ngOnChanges to not be called (since no new reference is assigned) and ngDoCheck to be called with this.from1 to have the data {'name': 'name0'} and the view still displaying name0

But instead

ngDoCheck is called with this.from1 equal {'name': 'name-0-2-2'}

and the view is displaying name0

My question is the following

  1. Why is the property this.from1 changed?

  2. And if the model is changed why is the view inconsistent with the model?


Solution

  • You're using ChangeDetectionStrategy.OnPush for the App2 which means that CD will not be triggered for App2 unless the input binding [from1]="from" is updated.

    So when you update the name property here the name is updated:

    changen0() {
        this.from.name = 'name-0-2-2'
    }
    

    you don't update the reference to this.from and hence change detection is not triggered for the App2 so you don't see the change in the HTML. ngOnChanges is not called as well.

    ngDoCheck is called with this.from1 equal {'name': 'name-0-2-2'}

    ngDoCheck doesn't mean your component being checked. It's called when Angular checks the parent component. Read this article for more information: