TanguyB
TanguyB

Reputation: 2006

RXJS - Multiple observable calls on array of properties

I'm kind off new to rxjs, and I've a fairly simple case that I can't work out in angular.

I've a http call to retrieve a group, this group contains a list of "buddy ids", a list of "genre ids" and a "region id".

To get the buddy objects trough the "buddy ids", I have to make a call to an API trough my service ( which return 1 buddy with given id ).

To get my genre objects, I can retrieve them form my store, same for region.

The problem is I can't have a solution worked out with pipes.

this.buddyService.getGroup(groupId).pipe(
    mergeMap((group) => forkJoin(
          mergeMap(() => forkJoin(group.buddyIds.map((id) => this.buddyService.getBuddy(id)))), 
          mergeMap(() => forkJoin(group.genreIds.map((id) => this.store.select(GenreState.genre(id))))),
          switchMap(() => this.store.select(RegionState.region(group.regionId))))
    ), takeUntil(this.destroyed$)
).subscribe(([group, buddies, genres, region]) => {

});

So the first mergeMap will be to get all the results togehter in 1 subscribe, then I've 3 sub calls to get the buddies, genres and the region.

I'd like to have my initial response ( the group ) and the results of the 3 subcalls in 1 subscribe, obviously this solution doesn't work, and I can't seem to get something else to work.

Any help would be appreciated, thanks !

Upvotes: 7

Views: 9311

Answers (2)

SnorreDan
SnorreDan

Reputation: 2910

This version is a little more verbose, but might be easier to reason about as it is divided into several chunks. Haven't tested it though.

import {combineLatest} from 'rxjs';

...

  const group$ = this.buddyService.getGroup(groupId);
  const buddies$ = group$.pipe(mergeMap(group => combineLatest(group.buddyIds.map((id) => this.buddyService.getBuddy(id))));
  const genres$ = group$.pipe(mergeMap(group => combineLatest(group.genreIds.map((id) => this.store.select(GenreState.genre(id))));
  const region$ = group$.pipe(switchMap(group => this.store.select(RegionState.region(group.regionId)))));

  combineLatest([group$, buddies$, genres$, region$])
    .pipe(takeUntil(this.destroyed$))
    .subscribe(([group, buddies, genres, region]) => {});

Upvotes: 2

kos
kos

Reputation: 5364

Seems you have too many inner mergeMaps. Try this

this.buddyService.getGroup(groupId).pipe(
  mergeMap((group) => forkJoin(
    of(group),
    forkJoin(...group.buddyIds.map((id) => this.buddyService.getBuddy(id))), 
    forkJoin(...group.genreIds.map((id) => this.store.select(GenreState.genre(id)))),
    this.store.select(RegionState.region(group.regionId)))
  ),
  takeUntil(this.destroyed$)
)
.subscribe(([group, buddies, genres, region]) => {});

The main issue was that you've combined "constructing" methods that create Observables (like forkJoin) with operators that operate on existing Observables (like switchMap or mergeMap).

Test the difference yourself: switchMap vs forkJoin

I've written this w/o deep testing, so it might not work immediately :)

Upvotes: 5

Related Questions