1

I have two api requests, the second one depends on the first one. The first request gets an array of 3 facilities. After this i need to do an api request for each facility to get an image which i need. I need the uuid of the facilities. I thought this could be easy with mergeMap. But i have 2 problems and can't find a solution: inside merge map, i thought service will be one service of the array but it is the whole array. Also I need to subscribe to getImage() too and store the value inside service.image.

getNewestNursingServices() {
  return this.http.get('api/nursing-service/newest').pipe(
   mergeMap(service => this.getImage('PREVIEW', service.uuid))
  );
}
getImage(type: string, nursingService: string): Observable<Image> {
  return this.http.get<Image>('api/nursing-images/' + type + '/' + nursingService);
}
keschra
  • 289
  • 1
  • 3
  • 17

2 Answers2

2

You can use forkJoin or concat:

getNewestNursingServices() {
  return this.http.get('api/nursing-service/newest').pipe(
   mergeMap((service: any[]) => {
     return concat(...service.map(s => {
       return this.getImage('PREVIEW', s.uuid)
     }) 
   }) 
  );
}
Julius Dzidzevičius
  • 10,775
  • 11
  • 36
  • 81
  • I would advise `switchMap` and `forkJoin`/`combineLatest`. – Roberto Zvjerković Feb 04 '20 at 16:43
  • Agree that `forkJoin` / `combineLatest` **might** be a better option, but for `siwtchMap` I couldn't agree. On every `getNewestNursingServices` invocation, new source is created, so cancellation wouldn't happen anyway and `mergeMap` is a lighter operator – Julius Dzidzevičius Feb 04 '20 at 16:58
  • It's just that switchMap sounds much better suited. Maybe sometimes you swap the http call for a Subject, now the logic doesn't suit the needs. I'd still use switchMap – Roberto Zvjerković Feb 04 '20 at 17:06
  • `switchMap` throws away the results of ongoing request, if another event comes from upstream. That suits the well known search-as-you-type problem, but here the OP wanted the result of all requests. – Alex Biro Feb 04 '20 at 17:09
  • Well, agree that its debatable @ritaj :) – Julius Dzidzevičius Feb 04 '20 at 17:10
  • @Alex Biro - there is no upstream, just a function call which creates a new stream every time. So no cancellation of the previous would happen – Julius Dzidzevičius Feb 04 '20 at 17:11
  • "All requests" is just one. And the function is called "newest" services, discarding *older* services sounds exactly what OP wanted. – Roberto Zvjerković Feb 04 '20 at 17:12
  • @JuliusDzidzevičius that's correct, it wouldn't matter here, but the point of `switchMap` is that it cancels the ongoing request, so if you don't rely on it, I think you should not use `switchMap`. But agree, this is a bit subjective. – Alex Biro Feb 04 '20 at 17:15
0

I hope I got the question right.

So your first problem was that the first api call returns an array. That can be solved by mergeMap-ing the flattened array, so the downstream observable will emit the 3 services consecutively.

getNewestNursingServices() {
  return this.http.get('api/nursing-service/newest')
    .pipe(
      mergeMap((services: []) => {
        // `of` will return an observable which emits the items of the array after each other
        return of(services);
      }),
      mergeMap(service => this.getImage('PREVIEW', service.uuid)),
      tap((image: Image) => {
        // here you can do sideeffects with the images, eg. pushing them into an array somewhere...
      }),
      reduce(
        (allImages: Image[], currentImage: Image) => {
          // ... or you can collect them into an array, so the downstream will be `Observable<Image[]>`
          allImages.push(currentImage);
          return allImages;
        },
        [],
      ),
    );
}

Regarding the fact that you have to subscribe, that's not true, you can use the resulting observable for example in an async pipe, if you don't want to do the sideeffect+subscription pattern.

Alex Biro
  • 1,069
  • 1
  • 14
  • 27