Alex
Alex

Reputation: 2072

Angular - merge multiple consecutive API calls into one result

I'm in the process of learning rxjs and currently facing something I'm not quite sure how to tackle. I need to call multiple API requests, but the issue is that first I need to get a list of names, and then, for each name get the rest of the data. So far I've come up with two map functions, the first call will get list of names, and the second map will iterate over the list and call another request to get the rest of the data. I assume this is bad practice but I cannot understand how to use mergeMap or forkjoin to achieve a better functioning code.

  public getNames(): Observable<any> {
    return this.httpClient.get<response>(someUrl)
                          .pipe(map(res => res.results), map((data)=>{
                              data.forEach(dat => {
                               this.getDataByName(dat.name).subscribe(res => {
                                  dat.image = res.image;
                               });
                              });
                              return data;
                            }));
  }
  
  public getDataByName(name): Observable<any> {
    return this.httpClient.get<any>(urlToGetMoreDataByName)
                          .pipe(map(res => res.results));
  }

Upvotes: 0

Views: 4134

Answers (2)

Darshan Malani
Darshan Malani

Reputation: 476

 this.campaignService.getCampaignDetailById(this.data.campaignId).subscribe((response: any) => {
      if (response && response.data && response.data.length > 0) {
        this.campaignService.getCampaignSessionDetailById(response.data[0].session_id).subscribe((response: any) => {
          if (response && response.data && response.data.length > 0) {
            // second param
            this.campaignMockupCatalog = response.data[0]['shrt-campaign-mockup-catalogs'];

            let designMockupTypeId = response.data[0]['shrt-campaign-mockup-catalogs'].map((data) => data.design_mockup_type_id);
            this.campaignService.getMockupTypes().subscribe((response: any) => {
              if (response && response.data && response.data.length > 0) {
                let shrtCampMockupType = response.data.find((data) => data.id == designMockupTypeId);
                // first param
                this.campaignMockupTypes = shrtCampMockupType['shrt-campaign-mockup-type'].map((data) => data);
              }
            });
          }
        })
      }
    })

Upvotes: 0

Picci
Picci

Reputation: 17762

If I understand right your problem, I would try something along these lines

// remove the specification of the return type (: Observable<any>) since you
// should be able to achieve a more precise result using Typescript 
// type inference
    
public getNames() {
  return this.httpClient.get<response>(someUrl).pipe(
        map(res => res.results),
        // transform "data", which is an array of names in an array of Observables
        // this first map is the rxjs map operator
        map((data)=> {
           // this inner map is the javascript method of the array
           // for each dat it returns an Observable which emits when getDataByNme returns
           return data.map(dat => this.getDataByName(dat.name).pipe(
                                    // this is an rxjs map operator which retuns a
                                    // dat object with the image property set
                                     map(res => {
                                       dat.image = res.image;
                                       return dat
                                     })
                                  )
           )
       }),
       // concatMap is used here to make sure that we fire the execution of the 
       // Observable created by forkJoin after the first get operation has returned
       concatMap(arrayOfObservables => forkJoin(arrayOfObservables))
     );
}

Now getNames returns an Observable which emits once all the calls to fetch the images have been executed. Using forkJoin we make sure we launch all the http get operations in parallel. We could use other operators, e.g. `mergeMap, if we wanted to control the concurrency level.

You may find some inspiration about which operators can be possibly used with http operations in this article.

Upvotes: 1

Related Questions