Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 Observable with ChangeDetectionStrategy.OnPush and async pipe are not working

Tags:

I am using an observable to return a list from service to my component and in my component I am using ChangeDetectionStrategy.OnPush and in template I am using async pipe, hoping this would give some performance benefit since Change Detection isn't performed all the time but only when new contents are available.

Below is my service:

import { Injectable, Inject, EventEmitter } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class ListService {

    public _list$: Subject<any[]>;

    private list: any[] = [];

    constructor() {
        this._list$ = <Subject<any>>new Subject();
    }

    get list$() {
        return this._list$.asObservable();
    }

    loadList() {
        //if this.list is populated through an http call the view is updated, 
        //if its a static list like below, it doesn't trigger view update.
        //this.list = ['Red', 'Green', 'Yellow', 'Blue'];
        this._list$.next(this.list);
    }
}

In my component I have:

import { Component, ChangeDetectionStrategy } from '@angular/core';
import { Observable } from 'rxjs/Observable';

@Component({
    templateUrl: `
<ul>
        <li *ngFor="let item of (list$ | async);">
            {{item}}
        </li>
    </ul>
`,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ListComponent {

    public list$: Observable<any[]>;

    constructor(
        private _ls: ListService
    ) {}

    ngOnInit() {
        this.list$ = this._ls.list$;
        this._ls.loadList();
    }
}

The issue if the loadList contents fetch list contents through an http call, the view is updated, if the list content are static, the view isn't updated.

If I wrap list update inside setTimeout, it does trigger view update

setTimeout(() => {
    this._list$.next(this.list);
}, 1);

I just started exploring Observables, can anyone please guide whats wrong with above code?

like image 364
Naveed Ahmed Avatar asked Jul 02 '16 15:07

Naveed Ahmed


People also ask

Does Async pipe triggering change detection?

Async pipe does't trigger changeDetection and do not redraw value in UI.

What is the difference between OnPush and default change detection?

OnPush means that the change detector's mode will be set to CheckOnce during hydration. Default means that the change detector's mode will be set to CheckAlways during hydration.

How does OnPush change detection work?

An OnPush change detector gets triggered in a couple of other situations other than changes in component Input() references, it also gets triggered for example: if a component event handler gets triggered. if an observable linked to the template via the async pipe emits a new value.

How pipe change detection works for primitive and references objects?

By default, pipes are defined as pure so that Angular executes the pipe only when it detects a pure change to the input value. A pure change is either a change to a primitive input value (such as String , Number , Boolean , or Symbol ), or a changed object reference (such as Date , Array , Function , or Object ).


1 Answers

Just move the code fron ngOnInit() to the constructor. Angular tries to resolve the binding (subscribe to the observable) before ngOnInit() is called and fails because thod._list$ is null and doesn't try later because it doesn't recognise the change.

like image 52
Günter Zöchbauer Avatar answered Sep 27 '22 20:09

Günter Zöchbauer