Jesper
Jesper

Reputation: 747

Angular - Multiple conditional http requests

I'm struggling a bit with coming up with a good solution for doing multiple http requests, based on the result of each of them. So far, I've been doing good with switchMap, as long as it's only 2 http requests. However, I've found out I need to perform 3, and I'm not sure how to do it with switchMap, or if there's a better solution for it.

Basically, I will await a response, and if it comes back empty, I want to perform a 2nd request, and if that comes back empty, I want to perform a 3rd request.

Let me try to give a simplified example of what I've gotten so far:

getData(): Observable<number> {
  const $subject = new Subject<number>();

  // loadData() takes in an id to specify what data to load
  service.loadData(1).pipe(
    switchMap(response => {
      if(response.length === 0) {
        return service.loadData(2);
      }
      else {
        return of(response);
      }
    }),
  ).subscribe(result => {
    $subject.next(result);
    $subject.complete();
  });
  return $subject.asObservable();

Now this works just fine, my issue is, that calling "loadData(2)", might also give back a response with length = 0, and in that case, I need to call loadData(3).

I tried having another switchMap within the switchMap, but that didn't seem to work properly. So my question is, how would I be able to check the length of the response of loadData(2), and if it's 0, return service.loadData(3) ?

Any help or tips are very welcome :)

Upvotes: 3

Views: 1446

Answers (2)

robert
robert

Reputation: 6152

One possible solution is to use from with pipe

and takeWhile -> concatMap -> filter -> tap operators.

getData in service will receive an array of id numbers to try getting data for.

from will emit each id from the ids array.

concatMap does not subscribe to the next observable until the previous completes.

filter will ignore empty results.

takeWhile will cancel the whole stream once take becomes false

  getData(ids: number[]): Observable<number[]> {
    let take = true;
    return from(ids).pipe(
      takeWhile(() => take),
      concatMap(id => this.getDataFromApi(id)),
      filter(result => result.length > 0),
      tap(() => take = false)
    );
  }

Sample usage in the component:

  handleGetData() {
    console.log("component: getData");
    const ids = [1, 2, 3, 4];
    this.dataService.getData(ids).subscribe(data => {
      console.log("got data: ", data);
    });
  }

Working stackblitz

Upvotes: 0

Barremian
Barremian

Reputation: 31125

One straight forward and ugly way would be to subscribe to each call individually and check it's result.

getData(): Observable<number> {
  const $subject = new Subject<number>();

  service.loadData(1).subscribe(
    loadOneResponse => {
      if(loadOneResponse.length === 0) {
        service.loadData(2).subscribe(
          loadTwoResponse => {
            if(loadOneResponse.length === 0) {
              service.loadData(3).subscribe(
                loadThreeResponse => {
                  $subject.next(loadThreeResponse);
                  $subject.complete();
                },
                loadThreeError => {
                }
              );
            } else {
              $subject.next(loadTwoResponse);
              $subject.complete();
            }
          },
          loadTwoError => {
          }
        );
      } else {
        $subject.next(loadOneResponse);
        $subject.complete();
      }
    },
    loadOneError => {
    }
  );

  return $subject.asObservable();
}

Upvotes: 2

Related Questions