arturh arturh - 7 days ago 5
TypeScript Question

ngrx: how to combine observables for use in async pipe

I'm trying to have a shadow copy of pending changes using observables and ngrx and I'm running into a little problem I don't understand:

export class SearchBoxContainerComponent {
filterSettings$: Observable<FilterSettings>;
filterChanges: {[key:string]: any};
filterChanges$: Subject<{[key:string]: any}>;
constructor(private store: Store<fromRoot.State>) {
this.filterChanges = {};
this.filterChanges$ = new Subject();
this.filterSettings$ = Observable.combineLatest(
store.let(fromRoot.getFilterSettings),
this.filterChanges$,
(filterSettings, filterChanges) => {
return Object.assign({}, filterSettings, filterChanges);
}
);
this.filterChanges$.subscribe(foo => {
console.log('filterChanges$: ', foo);
});
this.filterSettings$.subscribe(foo => {
console.log('filterSettings$: ', foo);
});
this.filterChanges$.next(this.filterChanges);
}

updateSetting(key, value) {
this.filterChanges = Object.assign({}, this.filterChanges, {[key]: value});
this.filterChanges$.next(this.filterChanges);
}

submitSearchbox() {
// TODO send ngrx action to really update the filterSettings
}
}


Here i use Observable.combineLatest instead of directly using

this.filterSettings$ = store.let(fromRoot.getFilterSettings);


to get an observable from the ngrx store.

The problem I have is that when my searchbox opens everything is null at first. Only after updating a value everything gets populated. If i directly bind it with store.let it works.

I use the async pipe in my HTML like this

<my-component [filterSettings]="filterSettings$ | async"></my-component>


But it is in an *ngIf so only get evaluated after the searchbox opens.
My guess it that the async pipe subscribes after all the action happened and with no new events it does not get a value. But why does it work with store.let then? It that a different observable that always gives you a value?

Question is what am I doing wrong, I get the impression I'm still missing something... bonus question: Is this a good way to go about having shadow copies of data with can be aborted or submitted?

Answer

Use rxjs/BehaviorSubject instead of a plain rxjs/Subject. It will feed the last item received to new subscribers.

this.filterChanges = {};
this.filterChanges$ = new BehaviorSubject(this.filterChanges);

You don't need to next() after, because it takes a value on the constructor. Observable.combineLatest() should work as expected by then.