2

I have to do 3 dependent request to an API.

  1. The first retreive an array of user id's.
  2. The second have to iterate over the user id's array and for each retreive an array of project id's related to the user.
  3. The third have to iterate over the project id's array and retreive data related to projects.

I want this kind of result:

[{'username': 'richard', projects: [{"id": 1, "name": "test"}]}, ...]

But i'm totally stuck with mergeMap, forkJoin etc..

The thing i have tried:

getUserTeamMembers(): Observable<any> {
return this.http.get(`${environment.serverUrl}/user/profil`).pipe(
  mergeMap((teamUsers: any) =>
    this.http.get(
      `api/user/${teamUsers.data._id}/team`
    )
  ),
  mergeMap((teamUsers: any) =>
    forkJoin(
      ...teamUsers.data.map((user: any) =>
        this.http.get(`api/user/${user.id}`)
      )
    )
  ),
  map((res: any) =>
    res
      .map((user: any) => {
        if (user.data) {
          return {
            firstname: user.data.local.firstname,
            lastname: user.data.local.lastname,
            email: user.data.local.email,
            projects: user.data.local.projects,
            language: user.data.local.lang
          };
        }
      })
      .filter((user: any) => user !== undefined)
  ),
  tap(t => console.log(t)),
  catchError(err => throwError(err))
);}

I try so many things, but i have no clue where to populate my array of projects which is currently only contains id's, not data related:

[{'username': 'richard', projects: ['id1', 'id2']}, ...]

1 Answer 1

1

have made a similar example hope it clear the idea.

of course did't use http instead made it local using of

and stored the final result in an array called results

we have a source to get the list of all users, another source that given the user id it will return the list of this user's projects' ids projectList, and finally a source that give one project id will returns it's details(projcectsDetails)

    let users = of([ 1, 2, 3 ])
    let projectsList = (userId) => of([ userId, userId*2 ]) 
    let projectsDetails = (projectId) => of({ id: projectId, details: `details about ${projectId}` })


    let results = [];

    users
    .pipe(
      tap(users => {
        users.forEach(userId => results.push({ userId, projects: [] }));

        return users
      }),
      switchMap(users => forkJoin(users.map(userId => projectsList(userId)))),
      switchMap(projectsArray => forkJoin(
          projectsArray.map(
            oneProjectArray => 
            forkJoin(oneProjectArray.map(projectId => projectsDetails(projectId)))
          )
        )
      ),
      tap(projectDetailsArray => {
        projectDetailsArray.forEach((projectsArray, index) =>{
          results[index]['projects'] = [ ...projectsArray ]
        })
      })
    )
    .subscribe(() => console.warn('final',results))

explanation

  1- initalize the result array from the given users
  [ 
   { userId: X, projcts: [] }, 
   { userId: Y, projcts: [] }, 
    ....
  ]

  2- for each give users we want the projects ids he/she has
  so the first switchMap will return (in order)
  [
   [ 1, 2, 3, ...] project ids for the first user,
   [ 8, 12, 63, ...] project ids for the second user,
   ...
  ]

  3- in the second switchMap we iterate over the previous array
  (which is an array that each item in it is an array of projects ids)

  so we needed two forkJoin the outer will iterate over each array of projects ids(the big outer 
  array previously mentioned)
  the inner one will iterate over each single project id and resolve it's observable value(which is the
  project details)

  4- finally in the tap process we have an array of array like the 2nd step but this time each
  array has the projects details and not the projects ids

  [
   { userId:1, projects: [ { id:1, details: 'details about 1' }, { id:2, details: 'details about 2' }]},
   { userId:2, projects: [ { id:2, details: 'details about 2' }, { id:4, details: 'details about 4' }]},
   { userId:3, projects: [ { id:3, details: 'details about 3' }, { id:6, details: 'details about 6' }]}
  ]
Sign up to request clarification or add additional context in comments.

8 Comments

Excelent answer and nice explanation but I think the taps blow up the answer unnecessary. Just an opinion :)
thank you for your time, i have an error now, when i do the last 2 forkJoin, my db gets called correctly but all my requests were cancelled, so when i finally tap there is nothing in the result.
@amerej sorry, my fault. switchMap will cancel previous requests or events, so try mergeMap instead of the second switchMap, if it works tell me to update the answer.
@LouayAlosh yep, it works with merge map but now, i don't find a way to get the result array created, if i return results in tap it returns the good result but as many times as he found results in the last mergeMap, if i don't return results it returns me an array of undefined :(
@amerej the question is does projectDetailsArray in the last tap return what you want in the order you need?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.