Search code examples
rxjsrxjs7

How to spread a nested observable


Say I want to subscribe to 3 observables A B and C, where some effect depends on the results of A and B, and some effect depends on the results of A B and C. The way I know how to do this is:

forkJoin({
      aAndB: forkJoin({
        a: of('a'),
        b: of('b')
      }).pipe(
        tap(aAndB => {
          //do something with aAndB.a and aAndB.b
        })
      ),
      c: of('c')
    }).subscribe(allthree => {
      console.log(allthree);
    });

Current output:

{
  aAndB: {
    a: 'a',
    b: 'b'
  },
  c: 'c'
}

But my desired output is to have aAndB spread/flattened like this:

{
  a: 'a',
  b: 'b',
  c: 'c'
}

Any way to achieve this?


Solution

  • I would use combineLatest to create independent observables for each set of values you care about. You can use the same sources in each declaration:

    a$ = of('a');
    b$ = of('b');
    c$ = of('c');
    
    ab$  = combineLatest({ a: a$, b: b$ });
    abc$ = combineLatest({ a: a$, b: b$, c: c$ });
    
    ab$.subscribe(({a, b}) => { 
      // side effect AB 
    });
    
    abc$.subscribe(({a, b, c}) => { 
      // side effect ABC
    });
    

    Since ab$ and abc$ both use a$ and b$ you may want both subscribers to share a single subscription (in case these observables were doing some kind of work you don't want repeated, like making a network call).

    You can accomplish this by using shareReplay:

    a$ = of('a').pipe(shareReplay(1));
    b$ = of('b').pipe(shareReplay(1));