1

I need some explanations about concatenation / piping / grouping of nested Observables.

I have a remote API /foo/bars/100 that replies with the following json object

{ 
    id: 100,
    members: [{memberID: 1},..,{{memberID: N}]
}

I have another API /members/${id} that returns the following json:

{
    id: ${id}
    values: ['bla bla bla',...]
}

I would like to call (GET) the api /foo/bars/100 , and then iterate over members and for each of them call (GET) /members/${id} and then build the result object.

I would like to obtain the following result:

{
    id: 100,
    members: [
        {
            memberID: 1,
            data: {
                id: 1,
                values: ['bla bla bla', ...],
            }
        },
        {
            memberID: 100,
            data: {
                id: 100,
                values: ['bla bla bla', ...]
            }
        }
    ]
}

What is the best pratice to obtain the result?

Alex Peters
  • 2,601
  • 1
  • 22
  • 29
Matz MaT
  • 71
  • 1
  • 5
  • Possible duplicate of [Chaining Observables in RxJS](https://stackoverflow.com/questions/37771855/chaining-observables-in-rxjs) – ggradnig Sep 17 '18 at 15:20

3 Answers3

1

This is my solution. It works as expected.

Would you have any tips for improving it?

 return dataService.getFooBars(100)
    .pipe(
        mergeMap((item) => {
            const members = _.map(item.members, member => {
                return  dataService.getMemberByID(member.memberID)
                    .pipe(
                        map((memberData) => {
                            member.data = memberData;
                            return member;
                        })
                    );
            });

            return forkJoin(...members).pipe(
                map(members => {
                    item.members = members;
                    return item;
                })
            );
        })
    )
Matz MaT
  • 71
  • 1
  • 5
0

If you want all the member's data to be loaded at once you can use forkJoin once members are loaded. You can also use Promise.all()

Shivang Gupta
  • 3,139
  • 1
  • 25
  • 24
0

I'm just learning RxJS observables myself and I enjoyed working on your question as a personal exercise. I haven't tested this, but I think this should be pretty close to how you could transform your input data to your desired output fashion in a linear, functional way:

const fooBarID = 100;
const membersObservable
    // { id: 100, members: [ {memberID: 1}, ..., {memberID: N} ] }
    = dataService.getFooBars(fooBarID)
        .pipe(
            // => [ {memberID: 1}, ..., {memberID: N} ]
            map(response => response.members),
            // => [ 1, ..., N ]
            map(listOfObjs => listOfObjs.map(obj => obj.memberID),
            // => [ Observable<APIMember>(1), ..., Observable<APIMember>(N) ]
            map(memberIDs => memberIDs.map(
                memberID => dataService.getMemberByID(memberID)
            )),
            // => Observable<APIMember[]>
            switchMap(memberObservables => forkJoin(...memberObservables)),
            // => [ Member(1), ..., Member(N) ]
            map(apiMembers => apiMembers.map(
                apiMember => ({ memberID: apiMember.id, data: apiMember })
            )),
            // => { id: 100, members: [ Member(1), ..., Member(N) ] }
            map(members => ({ id: fooBarID, members }))
        );

If you try it out, I'd be keen to hear back from you as to how it worked (or failed).

Alex Peters
  • 2,601
  • 1
  • 22
  • 29