Home > Mobile >  Nested mergeMap forkJoin combinations
Nested mergeMap forkJoin combinations

Time:07-08

I'm working with data of the form tool > feature > view. In other words, there are tools, a tool has features, a feature has views.

In Angular 2 (13), I'm using a service to handle http calls to fetch data. When the app starts, a resolver triggers the setting of the service's tools array property by calling the service's setTools() method. This method should build tools by fetching the tools, then populating each tool with features, which are each populated with views, all via nested http calls for each part.

I found an implementation using a combination of mergeMap and forkJoin (from rxjs) that achieves this down a single layer. I then implemented the following in the service:

setTools(): Observable<Tool[]> {
  return this.getTools().pipe(
    mergeMap(tools =>
      forkJoin<Tool[]>(
        tools.map(tool =>
          this.getFeatures(tool.id).pipe(
            map(features => ({ ...tool, features })
          )
        )
      )
    ),
    tap<Tool[]>(tools => console.log(tools))
  )
}

This works correctly. Tools are printed out, each containing its respective features. Great!

Naively, I implemented the following to achieve the next layer of population, i.e. views:

setTools(): Observable<Tool[]> {
  return this.getTools().pipe(
    mergeMap(tools =>
      forkJoin<Tool[]>(
        tools.map(tool =>
          this.getFeatures(tool.id).pipe(
            mergeMap(features =>
              forkJoin<Feature[]>(
                features.map(feature =>
                  this.getViews(feature.id).pipe(
                    map(views => ({ ...feature, views })
                  )
                )
              )
            ),
            map(features => ({ ...tool, features })
          )
        )
      )
    ),
    tap<Tool[]>(tools => console.log(tools))
  )
}

However, tools no longer log out to console. Why? What am I doing wrong? Also, if there is another way to implement this, I'll greatly appreciate the input. FYI, I'm quite new to web development.

CodePudding user response:

I could not get your example to work without more code/types, so I've changed the approach slightly and might not answer the question completely. Using mergeMap with mergeAll and to Array gives the nested structure.

  setTools() {
    return this.getTools().pipe(
      mergeAll(),
      mergeMap(tool =>
        this.getFeatures(tool.id).pipe(
          mergeAll(),
          mergeMap(feature=>
             this.getViews(feature.id).pipe(
                 map((views) => ({...feature, views }))
             )
          ),
          toArray(),
          map((features)=>({...tool, features}))
        )
      ),
      toArray(),
    );
  }

See stackblitz: https://stackblitz.com/edit/angular-ivy-ee1myv?file=src/app/app.component.ts

CodePudding user response:

I've just separated out your code into a few separate functions. It runs just fine for me on a minimalist harness. Basically no changes...

embellishFeature(feature){
  return this.getViews(feature.id).pipe(
    map(views => ({...feature, views}))
  );
}

embellishFeatures(features){
  return forkJoin(features.map(v => this.embellishFeature(v)));
}

embellishTool(tool){
  return this.getFeatures(tool.id).pipe(
    mergeMap(v => this.embellishFeatures(v)),
    map(features => ({...tool, features}))
  );
}

embellishTools(tools){
  return forkJoin(tools.map(v => this.embellishTool(v)));
}

setTools(): Observable<Tool[]> {
  return this.getTools().pipe(
    mergeMap(v => this.embellishTools(v)),
    tap(tools => console.log(tools))
  );
}
  • Related