3
votes

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?

1
I wouldn't keep another variable this.filterChanges just to keep the latest value from the subscriber. There's operators to implement updateSetting in a nicer way. I suggest you take this to Code Review.André Werlang
on further thought i moved all this logic in my ngrx reducer where it better fits i think because i need to access the shadow copy elsewhere ... but it was a good lesson on this whole observable stuff.arturh
that's a good way tooAndré Werlang

1 Answers

4
votes

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.