convert nested AngularJS service to Angular Observable service

I have some AngularJS(pre 1.5) services using nested calls in a project that we are rebuilding in Angular(11). The services use nested calls but I have no idea how to rebuild them using RXJS.

Any help, or detailed links to understand how I can get the result I need would be great. I have not been able to find anything sofar that helps me understand how to resolve this.

This is the original service:

function getBalanceGroups() {
            return $http.get(url.format("/accounts/{{accountId}}/balance-groups", $stateParams))
                .then(function (response) {
                    _.each(response.data, function (item) {
                        getBalanceViews(item.accountBalanceGroupId)
                            .then(function (balanceViewData) {
                                item.balanceViews = _.sortBy(balanceViewData, function (view) {
                                    return (view.balanceViewId === item.totalIndebtednessViewId) ? 0 : 1;
                                });
                                _.each(item.balanceViews, function (view) {
                                    getBalanceSegments(view.accountBalanceViewId)
                                        .then(function (balanceSegmentData) {
                                            view.balanceSegments = balanceSegmentData;
                                            view.totalBalance = 0;
                                            view.totalBalance = _.sumBy(view.balanceSegments, "balance");
                                        });
                                });
                            });
                    });
                    response.data = _.sortBy(response.data, function (item) {
                        return (item.isActive && item.isPrimary) ? 0 : 1;
                    });
                    return new LinkedList(response.data);
                }, function (error) {
                    $log.error('Unable to return balance group for the balanceChiclet');
                });
        }

This is what I have so far: (not working – it is returning the final api data response, I need to use the data to use the data to modify the previous response and return the modified data. No idea how )

getBalanceGroups(accountId: number | string): Observable<any> {
    let balGroupsUrl = `/accounts/${accountId}/balance-groups`;
    return this.http.get(`${this.baseUrl}${balGroupsUrl}`).pipe(
      mergeMap( (groups: any) => groups),
      flatMap((group:any) => {
        group.balanceViews = [];
        
        return this.getBalanceViews( group.accountBalanceGroupId, group )

      }),
      mergeMap( (views: any) => views),
      flatMap((views: any) => {
        return this.getBalanceSegments( views.accountBalanceViewId )
      }),

      catchError((err) => of(err) ),

      tap( groups => console.log('groups: 3:', groups) ),
    )

  }

private getBalanceViews(accountBalanceGroupId: number | string, group): Observable<any> {
    let balViewsUrl = `/balance-groups/${accountBalanceGroupId}/balance-views`;
    return this.http.get(`${this.baseUrl}${balViewsUrl}`);
  }

  private getBalanceSegments(accountBalanceViewId: number | string): Observable<any> {
    let balSegUrl = `/balance-views/${accountBalanceViewId}/balance-segments`;
    return this.http.get(`${this.baseUrl}${balSegUrl}`);
  }

Answer

  1. Instead of the mergeMap + flatMap (they are synonymous BTW), you could use forkJoin to trigger multiple requests in parallel.
  2. You might have to use multiple nested forkJoin given the nature of the request.
  3. While I’ve converted the loadash sumBy using Array#reduce, I’ve left the sort incomplete for you to do it.

Try the following

getBalanceGroups(): Observable<any> {
  return this.http.get(`/accounts/${accountId}/balance-groups`, { params: stateParams }).pipe(
    switchMap((response: any) =>
      forkJoin(
        response.data.map((item: any) => 
          getBalanceViews(item.accountBalanceGroupId, item).pipe(
            map((balanceViewData: any) => ({
              ...item,
              balanceViews: balanceViewData.sort()            // <-- incomplete
            })),
            switchMap((item: any) => 
              forkJoin(
                item.balanceViews.map((view: any) =>
                  getBalanceSegments(view.accountBalanceGroupId).pipe(
                    map((balanceSegmentData: any) => ({
                      ...item,
                      balanceSegments: balanceSegmentData,
                      totalBalance: view.balanceSegments.reduce((acc, curr) => acc += curr['balance'], 0)
                    }))
                  )
                )
              )
            )
          )
        )
      )
    ),
    map((response: any) => ({
      ...response,
      response.data: response.data.sort()                      // <-- incomplete
    })),
    catchError((error: any) => {
      console.error('Unable to return balance group for the balanceChiclet');
      return of(error);
    })
  );
}