Thibault J
Thibault J

Reputation: 4446

Create an Observable object from a list of Observables

I'm still wrapping my head around RxJS and there is this pattern I keep running into and that I would like to find a more elegant way to write.

Implementing the model part of a Model-View-Intent pattern component, I have a function that takes actions as input an returns a single state$ Observable as output.

function model(actions) {
    const firstProperty$ = 
    const anotherProperty$ = …

    // Better way to write this?
    const state$ = Rx.Observable.combineLatest(
        firstProperty$, anotherProperty$,
        (firstProperty, anotherProperty) => ({
            firstProperty, anotherProperty
        })
    );
    return state$;
}

So my model method computes a bunch of observables, every one of them emit items that represents a part of the state of my application. That is fine.

But how to I cleanly combine them into a single one observable that emits states, each state being a single object whose keys are the initial observable names?

Upvotes: 0

Views: 2069

Answers (2)

Thibault J
Thibault J

Reputation: 4446

Using help from CHadrien, here is a working solution.

const prop1$ = Rx.Observable.of('foo');
const prop2$ = Rx.Observable.of('bar');
const prop3$ = Rx.Observable.of('baz');
const prop4$ = Rx.Observable.of('foobar');

function combineObservables(objectOfObservables) {
  const keys = Object.keys(objectOfObservables);
  const observables = keys.map(key => objectOfObservables[key]);
  const combined$ = Rx.Observable.combineLatest(
    observables, (...values) => {
      var obj = {};
      for (let i = 0 ; i < keys.length ; i++) {
        obj[keys[i]] = values[i];
      }
      return obj;
    }
  );
  return combined$;
}
combineObservables({prop1$, prop2$, prop3$, prop4$}).subscribe(x => console.log(x));

And the result:

[object Object] {
  prop1$: "foo",
  prop2$: "bar",
  prop3$: "baz",
  prop4$: "foobar"
}

Upvotes: 1

mmai
mmai

Reputation: 745

I borrowed this pattern from https://github.com/cyclejs/todomvc-cycle :

function model(initialState$, actions){
  const mod$ = modifications(actions)

  return initialState$
  .concat(mod$)
  .scan( (state, mod) => mod(state))
  .share() 
}

function modifications(actions){
  const firstMod$ = actions.anAction$.map(anAction => (
    state => ({ ...state,
      firstProperty: anAction.something
    })

  const secondMod$ = actions.otherAction$.map(otherAction => (
    state => ({ ...state,
      firstProperty: otherAction.something,
      secondProperty: aComputation(otherAction)
    })

  return Rx.Observable.merge([firstMod$, secondMod$ ]).share()
}

In the main function :

const initialState$ = Rx.Observable.from({})
const actions = intent(DOM)
const state$ = model(initialState$, actions).share()

Upvotes: 1

Related Questions