Guy E
Guy E

Reputation: 1927

using mergeMap in forkJoin to get observables depending one on the other

I have a combination of several async request I need to complete together (doing it with forkJoin) - when doing so, I initiate an object:

    export class fullData
    {
        AProp  : any;
        BProp  : any;
        CProp  : any;
    }
    const fullDataObservable : fullData = forkJoin(
    {
        AProp : this.myFirstSrv.getAProp(), 
        BProp : this.mySecondsSrv.getBProp(),
        CProp : this.myThirdSrv.getCProp()
    });
    return fullDataObservable;      

So far - so good. Now - I have 3 new properties in this object:

    export class fullData
    {
        AProp  : any;
        BProp  : any;
        CProp  : any;
        prop1 : any;
        prop2 : any;
        prop3 : any;
        
    }

the first of then (prop1) depend on the CProp value (or this.myThirdSrv.getCProp()) as a parameter, when doing a get request, the second (prop2) is a simple manipulation of the first one, and the third one (prop3) is also depending on the second as a parameter in a get request. I've tried using a mergeMap, but it didn't work. What am I doing wrong ?:

    gatherAllRelevantData() : any
      {
        //this.myService.getProp1() return observable after get request 
        this.myService.getProp1().pipe
        (
          mergeMap((prop1Value : any[]) => 
          {
            //this.myService.getResultAfterSimpleManipulation return object, not observable
            let prop2Values : any[] = this.myService.getResultAfterSimpleManipulation(prop1Value);
            //the this.getProp3Value return observable aftet get request
            return this.getProp3Value(prop2Value).pipe(
              map(prop3Values => 
                {
                  return { prop1 : prop1Value, prop2 : prop2Values, prop3 : prop3Values };
                })
            )
          }
      ));
    }

This should all done in a resolve function and should initiate the fullData object, by getting data from the this.gatherAllRelevantData (not happening) and without unneeded execution of the this.gatherAllRelevantData:

    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any> 
    {
      const fullDataObservable = forkJoin(
      {
        AProp : this.myFirstSrv.getAProp(), 
        BProp : this.mySecondsSrv.getBProp(),
        CProp : this.myThirdSrv.getCProp(),
        prop1 : this.gatherAllRelevantData().map(item => items.prop1),
        prop1 : this.gatherAllRelevantData().map(item => items.prop1),
        prop1 : this.gatherAllRelevantData().map(item => items.prop1)
    });
    return fullDataObservable; 
  } 

Upvotes: 3

Views: 959

Answers (2)

Owen Kelvin
Owen Kelvin

Reputation: 15083

I would approach this problem by solving one property at a time then combining everything at the end.

  AProp = this.myFirstSrv.getAProp();
  BProp = this.mySecondSrv.getBProp();
  CProp = this.myThirdSrv.getCProp();
  prop1 = this.CProp.pipe(
    mergeMap(CProp => this.propsSrv.getProp1(CProp))
  )
  prop2 = this.AProp.pipe(
    map(AProp => this.propsSrv.getProp2(AProp))
  )
  prop3 = this.BProp.pipe(
    mergeMap(BProp => this.propsSrv.getProp3(BProp))
  )

  fullData$ = forkJoin({
    AProp: this.AProp,
    BProp: this.BProp,
    CProp: this.CProp,
    prop1: this.prop1,
    prop2: this.prop2,
    prop3: this.prop3,
  })

See link to a stackblitz demo

Upvotes: 0

Barremian
Barremian

Reputation: 31135

  1. If the class fullData is only used to hold the member variables, you could use an interface instead.

  2. Based on the conditions you've mentioned you could use RxJS switchMap (or mergeMap) operator along with the map operator to get the object of your requirement.

Try the following

export interface fullData {
  AProp: any;
  BProp: any;
  CProp: any;
  prop1: any;
  prop2: any;
  prop3: any;
}

const fullDataObservable: Observable<fullData> = forkJoin({ // <-- it's of type `Observable<fullData>`, not `fullData`
  AProp: this.myFirstSrv.getAProp(), 
  BProp: this.mySecondsSrv.getBProp(),
  CProp: this.myThirdSrv.getCProp()
}).pipe(
  switchMap(data =>
    this.someService.getProp1(data.CProp).pipe( // <-- get `prop1` that depend on `data.CProp`
      map(prop1 => ({
        AProp: data.AProp,
        BProp: data.BProp,
        CProp: data.CProp,
        prop1: prop1,
        prop2: this.simpleManipulate(prop1) // <-- get `prop2` that depend on `prop1`
      }))
    )
  ),
  switchMap(data =>
    this.someService.getProp3(data.prop2).pipe( // <-- get `prop3` that depend on `data.prop2`
      map(prop3 => ({
        AProp: data.AProp,
        BProp: data.BProp,
        CProp: data.CProp,
        prop1: data.prop1,
        prop2: data.prop2,
        prop3: data.prop3
      }))
    )
  )
)

return fullDataObservable;

Upvotes: 2

Related Questions