Reputation: 85
My backend has two graphql queries: familiyTreeQuery
which returns a family tree as a node with children, which can also have children and so on, and personPicturesQuery($name: string)
which returns pictures of the given person.
My goal is to call the familyTreeQuery
, iterate the tree and if the person is called Alice, then I want to call the personPicturesQuery
for the person and add the information to the node in the tree. This should be done for all Alices in the tree.
My problem is, that the call to fetch pictures happens asynchronous and the data is therefore returned before the information is added to the node.
I failed to use flatMap
as suggested in this question, because the call to fetch the pictures is happening while iterating the tree and I can't call it in the pipe method of the familyTreeQuery
.
public getContext(): Observable<TreeNode> {
return this.familyTreeQuery.watch().valueChanges.pipe(
map(result => {
const familyTree = result.data.familyTree;;
this.addPicturesToAlice(familyTree);
return familyTree;
})
)
}
private addPicturesToAlice(node: TreeNode) {
if (node.name === 'Alice') {
this.fetchPictures(node.id).subscribe(pictures => {
node.pictures = pictures;
})
}
if (node.children && node.children.length > 0) {
for (const childNode of node.children) {
this.addPicturesToAlice(childNode);
}
}
}
private this.fetchPictures(personId): Observable<Picture[]> {
return this.personPicturesQuery.fetch({id: personId}).pipe(
map(result => {
return result.data.personPictures;
})
)
}
From what I understand I'm not supposed to call subscribe in the addPicturesToAlice
method, but I'm new to Angular and RxJS and didn't find a way to make this work.
Upvotes: 1
Views: 308
Reputation: 6424
You can achieve that by creating an array of observables and passing it along recursively, then subscribing to it in getContext
using forkJoin
, as demonstrated below:
public getContext(): Observable<TreeNode> {
return this.familyTreeQuery.watch().valueChanges.pipe(
switchMap(({ data: { familyTree } }) => forkJoin(this.addPicturesToAlice(familyTree)))
)
}
private addPicturesToAlice(node: TreeNode, observables: Observable<Picture[]>[] = []): Observable<Picture[]>[] {
if (node.name === 'Alice') observables.push(
this.fetchPictures(node.id).pipe(tap(pictures => node.pictures = pictures))
)
if (node.children?.length) {
for (const childNode of node.children) {
this.addPicturesToAlice(childNode, observables);
}
}
return observables;
}
private fetchPictures(personId: number): Observable<Picture[]> {
return this.personPicturesQuery
.fetch({ id: personId })
.pipe(map(result => result.data.personPictures))
}
Hope it's clear enough.
Upvotes: 1