Search code examples
angularrxjsangular2-changedetection

Angular: Model listening with ChangeDetectionStrategy.OnPush


Let say I have one big Json model that my back-end sends to my front-end that would look like this:

{
     dataA: { //some object },
     dataB: { //some object },
     dataC: { //some object },
     ...
}

Now let say I have ComponentA that takes dataA as @Input(), ComponentB that takes dataB as @Input(), etc:

@Component({
    selector: 'comp-a'
})
class ComponentA {
    @Input() _dataA;
}

@Component({
    selector: 'comp-b'
})
class ComponentA {
    @Input() _dataB;
}

// .... other components

@Component({
    selector: 'app',
    template:`
        <comp-a [_dataA]="dataA"></comp-a>
        <comp-b [_dataB]="dataB"></comp-b>
        ...
    `
})
class AppComponent {
}

And that I want to make those components use the OnPush change detection strategy.

When a new model is received, it could happens that a data field in the model did not changed from its previous value in the previous model, so I would not like them to be passed again as @Input() to the component to avoid running change detection for nothing.

Is there some kind of clever way to detect change in my model on front-end side before passing its datas as @Input() to my components, and notify them only when their respective data changed ? Or should I let Angular perform the change detection itself ? Is OnPush really appropriate here ?


Solution

  • OnPush improves efficiency by not checking model properties and triggers an update when the instance of an object is changed, not the properties of the object. To do what you are proposing would involve inspecting the properties of your object to see if anything has changed. You would basically be reinventing change detection so I don't see the point and you would need to do it better than the Angular team have done it to see any benefit.

    You have also marked this question with rxjs but there is nothing about rxjs in the question. The best way to implement OnPush change detection is with rxjs observables and use the async pipe in your template. This way you only get the observable to emit the updated value.

    @Component({
        selector: 'app',
        template:`
            <comp-a [_dataA]="dataA$ | async"></comp-a>
            <comp-b [_dataB]="dataB$ | async"></comp-b>
            ...
        `
    })
    class AppComponent {
        dataA$ = new BehaviorSubject<DataAModel>(undefined);
        dataB$ = new BehaviorSubject<DataBModel>(undefined);
    
        updateA() {
          if (a has changed) { // If your detection to check changes is inefficient then there is no point
            this.dataA$.next(a);
          }
        }
    
        updateB() {
          if (b has changed) {
            this.dataB$.next(b);
          }
        }
    }