Search code examples
angularrxjsreactivex

Compose Observable from two dependant


I need to compose one Observable from values of two another Observables. The answer is zip operator. But my case is more complicated. I have an id of A.

A {id, name, idOfRelatedB} //first observable
B {...} // second

So, in order to get B, I need to get A before to retrieve an id of B. And finally I need to return an Observable with A and B. How to deal with that in RX.

My unsuccessful solution in angular/typescript:

    @Injectable()
export class StrategyResolver implements Resolve<StrategyWithSourceCode> {
  constructor(private strategyService: StrategyService) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
    const strategyId = route.paramMap.get('strategyId');
    let sourceCodeObs: Observable<SourceCode>;
    let strategy: Strategy;

    this.strategyService.getStrategy(parseInt(strategyId)).subscribe(value => {
      strategy = value;
      sourceCodeObs = this.strategyService.getStrategySource(parseInt(strategyId), strategy.sourceCodeId);
    });
    return zip(of(strategy), sourceCodeObs, (v1: Strategy, v2: SourceCode) => ({v1, v2}));
  }
}

Upd: correct solution with help of user madjaoue (thanks for him) with any specific of rxjs 6.

resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> {
    const strategyId = route.paramMap.get('strategyId');
    return this.strategyService.getStrategy(parseInt(strategyId)).pipe(
      concatMap(strategy => {
        let sourceCodeObs = this.strategyService.getStrategySource(parseInt(strategyId), strategy.sourceCodeId);
        return zip(of(strategy), sourceCodeObs, (v1: Strategy, v2: SourceCode) => ({v1, v2}));
      })
    )
  }

Solution

  • You weren't so far from the answer. This is a slight modification of your code that does what you want.

    const strategyObs = this.strategyService.getStrategy(parseInt(strategyId));
    
    // use concatMap to combine two streams
    strategyObs.concatMap(strategy => {
      let sourceCodeObs = this.strategyService.getStrategySource(parseInt(strategyId), strategy.sourceCodeId);
    
      // zip the response of the first service with the second 
      return zip(Observable.of(strategy), sourceCodeObs)
    });
    .subscribe( ([A, B]) => ...)
    

    Note that you can replace concatMap by other operators like switchMap , mergeMap or exhaustMap for example. All will combine your streams, but not in the same way. Please check that concatMap is suitable for your case.