Search code examples
angulartypescriptrxjsobservablestore

Problem with an observable that uses a second API


Well here I am really bad with RxJs and I am facing a problem.

In one of my Angular component I load data like this:

this._obs$.push(this.store1.loadTypes());
this._obs$.push(this.store2.loadLimitQuantities());
// [...]
    
forkJoin(this._obs$).subscribe(
  next => console.log(next),
  error => console.log(error)
);

One of the observables receives data from API 1 and then needs to retrieve data from API 2 from the data received from API 1:

loadLimitQuantities(): Observable<LimitQuantity[]> {
    // API 1
    const obs = this.limitQuantityService.getLimitQuantities().pipe(share());
    obs.subscribe(data => {
        if (data) {
            const ifs = [];
            data.forEach(element => {
                // API 2
                ifs.push(this.technicalAttribTextIFSService.getType(element.ifsType).pipe(take(1), tap((type) => { 
                    type ? element.type = type.valueText : element.type = null;
                })));
            });
            forkJoin(ifs).subscribe(end => {
                this.limitQuantitiesSubject.next(data);
            });
        } else {
            console.error("Erreur lors du chargement des quantités limites");
        }
    });
    // AP1
    return obs;
}

Unfortunately the next of my Angular component which loads the data is received too early because API 1 has finished returning the data but not API 2.

How to do ? The return should be at the forkJoin of API 2.

StackBlitz : here

Thanks for your help.


Solution

  • I'm not sure I got your problem 100% - but I think this will get you what you need.

    limitedQuantitiesWithTypeFromApi2$ = this.loadLimitQuantities()
        .pipe(
            switchMap(
              x => forkJoin(x.map(y => this.getType(y.ifsType))), 
            ),
          );
    
      ngOnInit() {
        forkJoin(this.loadTypes(), this.limitedQuantitiesWithTypeFromApi2$)
          .subscribe(([types, lq]) => console.log('Original 1', types, lq));
    
        forkJoin(this.loadTypes(), this.loadLimitQuantities())
          .pipe(
            map(([types, lq]) => {
              return lq.map(x => ({
                ...x, 
                type: types.find(y => y.ifsType === x.ifsType)
              }))
            })
          )
          .subscribe(x => console.log('Alternate Approach', x));
    
      }
    
      loadTypes() {
        return timer(1000).pipe(
          map(x => ([
            { ifsType: 1, type: 'vText1'}, 
            { ifsType: 2, type: 'vText2'}
          ])),
          share()
        );
      }
    
      loadLimitQuantities() {
        return timer(1000).pipe(
          map(x => ([
            {ifsType: 1, type: null, }, 
            {ifsType: 2, type: null }
          ])),
          share(),
        );
      }
    
      getType(ifsType: number) {
        return timer(1000).pipe(
          map(x => ifsType === 1 ? 'vText1' : 'vText2'),
          map(x => ({ ifsType, type: x })),
          share()
        );
      }
    

    However, I think it's safe to assume that your API2 is an HTTP call - and while it would work, I would advice to just batch fetch all the "type-associations" so you can just make a simpler stream.

    I mean if loadTypes already has an ifsType to type associaction then you can just do the "Approach 2" subscription - and if you're using angular anyway, you can just use the async pipe to subscribe to loadTypes and loadLimitedQuantities.