Home > Mobile >  Angular how to concatenate the results of many observable using concatMap
Angular how to concatenate the results of many observable using concatMap

Time:06-22

I wrote four api and i have decided to concatenate all their results using rxjs operator concatMap

All i was doing is to call them one by one , for each observable i push all the results and move to the next. Unfortunately i go so many syntax errors like Argument of type '(resTasks: any) => void' is not assignable to parameter of type '(value: any, index: number) => ObservableInput' because i was doing some treatement.

  listProjectWithConcatMap(projectId:number) :any {
    let issuesList:any = [];
    const headers = { 'Authorization': this.authService.getToken()};
      this.listProjectTasks(projectId).pipe(
        concatMap((resTasks) => {
         for(var item of resTasks.body) {
          issuesList.add(item);
         }
         this.listProjectSprints(projectId);
        })
        concatMap((resSprints) => {
          for(var item of resSprints.body) {
           issuesList.add(item);
          }
          this.listProjectSprints(projectId);
         }
        )
        concatMap((resEpics) => {
          for(var item of resEpics.body) {
           issuesList.add(item);
          }
          this.listProjectEpics(projectId);
         }
        )
        concatMap((resStories) => {
          for(var item of resStories.body) {
           issuesList.add(item);
          }
          this.listProjectStories(projectId);
         }
        )
      )
    return issuesList;
  }

I do not know how to fix the syntax problems but i am sure that using concatMap operator is a right choice for this situation.

example of listProjectTasks

 listProjectTasks(projectId:number) :Observable<any> {
    const headers = { 'Authorization': this.authService.getToken()};
    return this.http.get<any>(SERVER_BASE_URL this.PREFIX '/get/' projectId '/tasks/list', { headers });
  }

example of listProjectEpics

  listProjectEpics(projectId:number) :Observable<any> {
    const headers = { 'Authorization': this.authService.getToken()};
    return this.http.get<any>(SERVER_BASE_URL this.PREFIX '/get/' projectId '/epics/list', { headers });
  }

CodePudding user response:

If you just want to by pass the error and return the the pipe with all the concatenated observables then here's the syntax:

  listProjectWithConcatMap(projectId: number): Observable<any> {
    const issuesList: any[] = [];
    return this.listProjectTasks(projectId).pipe(
      concatMap((resTasks) => {
        for (var item of resTasks.body) {
          issuesList.push(item);
        }
        return this.listProjectSprints(projectId);
      }),
      concatMap((resSprints) => {
        for (var item of resSprints.body) {
          issuesList.push(item);
        }
        return this.listProjectSprints(projectId);
      }),
      concatMap((resEpics) => {
        for (var item of resEpics.body) {
          issuesList.push(item);
        }
        return this.listProjectEpics(projectId);
      }),
      concatMap((resStories) => {
        for (var item of resStories.body) {
          issuesList.push(item);
        }
        return this.listProjectStories(projectId);
      }),
      map(() => issuesList)
    );
  }
}

Some mistakes that you made that causes the syntax errors:

  • There's no such thing as add for array in Javascript, so we use push here instead
  • concatMap require that you return an observable at the end of the callback, which you did not, so we add return ... for all of them
  • Returning the array outside the pipe will just make the function return an empty array. What you want here is to return it as part of the pipe so that the function will return an observable of issueslList, therefor we use the map operator at the end.

This will work (I hope). But yeah it does not look great, there's no safe typing, error handling and the codes in general look kinda sus.

CodePudding user response:

Not RxJS expert, but you can achieve with:

import {
  concatMap,
  of,
  tap,
  map
} from 'rxjs';

const ob1 = this.listProjectTasks(projectId).pipe(
  tap((resTasks) => {
    for (var item of resTasks.body) {
      issuesList.push(item);
    }
  })
);

const ob2 = this.listProjectSprints(projectId).pipe(
  tap((resSprints) => {
    for (var item of resSprints.body) {
      issuesList.push(item);
    }
  })
);

const ob3 = this.listProjectEpics(projectId).pipe(
  tap((resEpics) => {
    for (var item of resEpics.body) {
      issuesList.push(item);
    }
  })
);

const ob4 = this.listProjectStories(projectId).pipe(
  tap((resStories) => {
    for (var item of resStories.body) {
      issuesList.push(item);
    }
  })
);

return of(ob1).pipe(
  concatMap((ob) => ob2),
  concatMap((ob) => ob3),
  concatMap((ob) => ob4),
  map((x) => issuesList)
);

Sample StackBlitz Demo

The reason why to use .tap() as the observable value doesn't need to transform the observable value. It is just doing the tasks to add values into issueList array. Lastly, transform the observable to return issueList.

Feel welcome to provide suggestions and improve the answer (if there is better/simpler way). Thanks.

  • Related