changeDetection: ChangeDetectionStrategy.OnPush doesn't seem to be working?
Asked Answered
F

3

6

https://stackblitz.com/edit/angular-xpamld

Question: Can someone help me understand why my prototype's changeDetection: ChangeDetectionStrategy.OnPush still allows me to update the inner value name? If this is not what ChangeDetectionStrategy.OnPush suppose to prevent, what should it be doing?

app.component.ts:

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent  {
  public name = 'Angular 5';
  public changeName() {
    this.name = 'Outer';
  }
}

app.component.html:

<hello name="{{ name }}"></hello>
<button (click)="changeName()">Outter Change</button>
<p>{{name}}-- outer</p>
<p>
  Start editing to see some magic happen :)
</p>

hello.component.ts:

@Component({
  selector: 'hello',
  template: `<h1>Hello {{name}}!</h1> <button (click)="changeName()">inner Change</button>`,
  styles: [`h1 { font-family: Lato; }`],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class HelloComponent  {
  @Input() name: string;


  public changeName() {
    this.name = 'Inner';
  }
}
Formant answered 4/2, 2018 at 7:19 Comment(0)
G
8

Because primitive datatype is immutable - if you change it, its reference also changes, so ChangeDetectorRefof your component knows it must detect changes (because OnPush looks for references changes, not data mutations in arrays, objects). If you want to avoid that on primitives, you can manually deactivate/activate this on ChangeDetectorRef instance with detach()/ reattach():

import { ChangeDetectorRef } from '@angular/core';

export class HelloComponent  {
  @Input() name: string;

 constructor(private ref: ChangeDetectorRef) {}

  public changeName() {
    this.ref.detach();
    this.name = 'Inner';
  }
}
Gasworks answered 4/2, 2018 at 7:44 Comment(6)
Just another qq how would you tell if changeDetection: ChangeDetectionStrategy.OnPush, is working properly? w/o detaching the ref? Marking correct regardless.Formant
@J.D. But then the inner change method will freeze and will not work any more ?Campo
@Matthew Harwood Here is a great article - blog.thoughtram.io/angular/2016/02/22/… basically when app state changes, ngZones fires an event that goes through ChangeDetectorReferences and invokes detectChanges() on eachSoutheast
@Rahul Singh - yes, because ChangeDetector runs on a whole component tree. And it will nor freeze it will just not change the view. You can perform logic on that methodSoutheast
Yes but that flaws the use of the inner method thow better to check for changes and act accordinglyCampo
@Rahul Singh - Maybe. I cant say what OP really needsSoutheast
S
3

The default change detection strategy is to be conservative and check all its bindings for something that might have changed. Typically, a change detection cycle is triggered whenever an [input] changes, or an (event) occurs from any component.

By changing a component's change detection strategy to OnPush, Angular only checks for updates when the component's inputs have actually changed. This allows Angular to be more efficient with change detection by allowing entire sub-trees to be skipped during change detection.

Here is a good article on it: https://vsavkin.com/immutability-vs-encapsulation-90549ab74487

Some points to help with the understanding is:

  1. By default, when a change detection digest is triggered, all bindings are checked for updates from all components, regardless of where the change originally came from.
  2. Change detection occurs to establish the original bindings (Input Bindings) or it can be triggered by a browser event (i.e. Output Bindings). Once triggered, #1 applies.
  3. Change detection occurs top-down, starting from the root component towards the leaves. Think of this as a unidirectional tree of connected components, that starts with an AppComponent for the root.
  4. When a component's change detection strategy is changed to OnPush then during change detection, it will skip a component's entire sub-tree if the inputs have not changed.
Semitic answered 4/2, 2018 at 8:7 Comment(0)
C
2

The state is updated only if parent view bindings changed and child component view was initialized with ChangeDetectionStrategy.OnPush.

In the Example you stated just add the following lines to the child Hello Component.

 ngOnChanges(simpleChange : SimpleChanges){
   console.log(simpleChange)
  }

You will see that that upon click of a button the view changes as the parent Bindings has changes that is the reason the view is updated in both parent and Child.

Campo answered 4/2, 2018 at 8:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.