eagor
eagor

Reputation: 10045

rxjs: access the result of original observable after it was switchMap'ped

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?

Upvotes: 2

Views: 69

Answers (3)

BizzyBob
BizzyBob

Reputation: 14750

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
            })
        ))
    ))
}

Upvotes: 1

user2216584
user2216584

Reputation: 5602

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
    })
);

Upvotes: 1

Rafi Henig
Rafi Henig

Reputation: 6422

Please consider the following example:

streamA$.pipe(
  switchMap(resultA => resultA ? streamB1$ : streamB2$),
  switchMap(resultB => loadData(resultB).pipe(map(x => [resultB, x]))),
  tap([resultB, data] => {})
);

Upvotes: 2

Related Questions