13
votes

In my Todo Cmp I have this code

this.todoListGroup$ = this.ngrx.select(fromRoot.getTodos)
    .flatMap((todos: Todo[]) => {
        console.log(todos)
        this.todos = todos
        this.ngrx.select(fromRoot.getLastChangedTodo);
    })
    .map(lastTodo =>{{
        console.log(lastTodo)
        doSomething(this.todos, lastTodo)
    })

When I subscribe to it I get one more console.log(lastTodo) each time todo changes. I figure that with flatmap and ngrx.select, I'm subscribing to a new Observable each time?

with which operator can I chain two store slices?

EDIT:

As long as the view is in the DOM, I want to stay subscribed to todoListGroup$ since it should keep updating my view.

My solution so far is to define a new slice in the reducer which returns the two desired properties. However, I'm still interested in which operator can effectively chain ngrx single property slices.

Thanks!

3
Could you clarify your goal? I am thinking that you want switchMap if you don't want multiple inner subscriptions or withLatestFrom if you want both values from store but the first should be the only trigger. - bygrace
is there a reason the 2 solutions I posted don't accomplish this? - bryan60
sorry, haven't tried them out yet :) I was working on my own so far. But the withLatestFrom seems promising! Thanks - Han Che
Rather than "define a new slice in the reducer which returns the two desired properties" you may be interested in creating projections using the reselect library or just raw rxjs. Here is a tutorial on creating projections with ngrx: gist.github.com/btroncone/… - bygrace

3 Answers

18
votes

Would something like this work:

this.todoListGroup$ =
    Observable.combineLatest(
        this.ngrx.select(fromRoot.getTodos), 
        this.ngrx.select(fromRoot.getLastChangedTodo)
    )
    .do(([todos, lastToDo]) => console.log(todos, lastToDo));

The do would execute each time either one of getTodos or getLastChangedTodo is updated and would take the latest known values from each of them at the time of the update. The caveat here is the order of when each of those updates are fired may not always be the same. So, if you wanted more of a chained (or cascaded) update then you could do this:

this.todoListGroup$ =
    this.ngrx.select(fromRoot.getTodos)
    .withLatestFrom(this.ngrx.select(fromRoot.getLastChangedTodo))
    .do(([todos, lastToDo]) => console.log(todos, lastToDo));

That will execute each time getToDos is updated and would take the latest value from getLastChangedTodo. Hence the chained (or cascaded) updated idiom.

edit for rxjs 5+ syntax:

this.todoListGroup$ =
    combineLatest(
        this.ngrx.select(fromRoot.getTodos), 
        this.ngrx.select(fromRoot.getLastChangedTodo)
    )
    .pipe(tap(([todos, lastToDo]) => console.log(todos, lastToDo)));


this.todoListGroup$ =
    this.ngrx.select(fromRoot.getTodos).pipe(
      withLatestFrom(this.ngrx.select(fromRoot.getLastChangedTodo)),
      tap(([todos, lastToDo]) => console.log(todos, lastToDo))
    );
28
votes

This is an approach for RxJs v6+ using the combineLatest operator.

First import the operators and Observable function like this:

import { Observable, combineLatest } from 'rxjs';
import { tap } from 'rxjs/operators';

Then you can use them like this:

combineLatest([
  this.store.pipe(select('users', 'userList')),
  this.store.pipe(select('users', 'accountsList')),
]).pipe(tap(([userList, accountsList]) => console.log(userList, accountsList)))
1
votes

Try this:

this.todoListGroup$ = this.ngrx.select(fromRoot.getTodos)
.flatMap((todos: Todo[]) => {
    console.log(todos)
    this.todos = todos
    this.ngrx.select(fromRoot.getLastChangedTodo);
})
.take(1)
.map(lastTodo =>{{
    console.log(lastTodo)
    doSomething(this.todos, lastTodo)
})