2

How do I access the resultB in the tap operator after it was switchMapped ?

streamA$.pipe(
    switchMap(resultA => {
        const streamB$ = resultA ? streamB1$ : streamB2$;

        return streamB$.pipe(                   // <- nesting
            switchMap(resultB => loadData(resultB)),
            tap(data => {
                // how do I access resultB here?
            })
        );

    })
);

bonus question: Is it possible to avoid the nesting here, and chain the whole flow under single pipe?

eagor
  • 9,150
  • 8
  • 47
  • 50
  • 1
    Hint: It's possible to access `resultA` in the `tap` where you want to access `resultB`. Why? The answer to this question should tell you what you need to do to access `resultB` in the `tap`. – frido Sep 14 '20 at 15:15

3 Answers3

2

Please consider the following example:

streamA$.pipe(
  switchMap(resultA => resultA ? streamB1$ : streamB2$),
  switchMap(resultB => loadData(resultB).pipe(map(x => [resultB, x]))),
  tap([resultB, data] => {})
);
Rafi Henig
  • 5,950
  • 2
  • 16
  • 36
1

This is how you can write your observable to get access of resultB and flat the observable operators chain -

streamA$.pipe(        
    switchMap(resultA => iif(() => resultA ? streamB1$ : streamB2$),
    switchMap(resultB => forkJoin([loadData(resultB), of(resultB)])),
    tap(([loadDataResponse, resultB]) => {
        //loadDataResponse - This will have response of observable returned by loadData(resultB) method
        //resultB - This is resultB
    })
);
Dharman
  • 30,962
  • 25
  • 85
  • 135
user2216584
  • 5,387
  • 3
  • 22
  • 29
1

The basic problem here is that switchMap is being used to transform values to emit a certain shape into the stream. If your resultB value isn't part of that shape, then operators further down the chain won't have access to it, because they only receive the emitted shape.

So, there are basically 2 options:

  • pass an intermediate shape, that contains your value
  • use a nested pipe to bring both pieces of data into the same operator scope

The solutions suggested so far involve mapping to an intermediate object. My preference is to use the nested pipe, so the data flowing through the stream is a meaningful shape. But, it really comes down to preference.

Using the nested pipe, your code would look something like this:

streamA$.pipe(
    switchMap(resultA => {
        const streamB$ = resultA ? streamB1$ : streamB2$;

        return streamB$.pipe(
            switchMap(resultB => loadData(resultB).pipe(
                tap(data => {
                    // you can access resultB here
                })
            ))
        );
    })
);

Note: you can use iif to conditionally choose a source stream:

streamA$.pipe(
    switchMap(resultA => iif(()=>resultA, streamB1$, streamB2$).pipe(
        switchMap(resultB => loadData(resultB).pipe(
            tap(data => {
                // you can access resultB here
            })
        ))
    ))
);

It can be helpful to break out some of the logic into separate functions:

streamA$.pipe(
    switchMap(resultA => doSomeWork(resultA)),
    miscOperator1(...),
    miscOperator2(...)
);

doSomeWork(result) {
    return iif(()=>result, streamB1$, streamB2$).pipe(
        switchMap(resultB => loadData(resultB).pipe(
            tap(data => {
                // you can access resultB here
            })
        ))
    ))
}
BizzyBob
  • 12,309
  • 4
  • 27
  • 51