0

So, I have server which on request returns array of objects

Example:

[
  {
   id: 1
   name: name1
  },
  {
   id: 2
   name: name2
  }
]

Array can have infinite elements;

After that I have to send new requests for each element in array with id of response.

example:

https://localhost/api/parent/${id}/child

response:

[
  {
   childId: 1
   name: name1
  },
  {
   childId: 2
   name: name2
  },
  {
   childId: 3
   name: name3
  }
]

And after that I have to send new requests for each response of new requests

example:

https://localhost/api/parent/${id}/child/${childId}/grandChild

response:

[
  {
   grandChildId: 1
   name: name1
  },
  {
   grandChildId: 2
   name: name2
  },
  {
   grandChildId: 3
   name: name3
  }
]

and response is similiar like others

and after that i have to organize data to look like this

[
  {
   id: 1
   name: name1
   children: [
            {
              childId: 1
              name: name1,
              grandChildren: [
                            {
                             grandChildId: 1
                             name: name1
                            },
                            {
                             grandChildId: 2
                             name: name2
                            },
                            {
                             grandChildId: 3
                             name: name3
                            }
                            ]
            },
            {
             childId: 2
             name: name2
            },
            {
            childId: 3
            name: name3
            }
        ]
  },
  {
   id: 2
   name: name2
  }
]

Each parent has array of children and each child has array of grandChildren.

This was my try but if i try in component to do parents.forEach(parent => console.log(parent.childern)) it is undefined

getData() {

return this.http.get<Parent[]>(baseUrl + 'parent', {withCredentials: true}).pipe(
      map(parents => {
        parents.forEach(parent => {
          this.getChildren(parent).subscribe(Children => {
            parent.Children = Children;
            parent.Children.forEach(Children => {
              this.getGrandChildren(Children).subscribe(grandChild => {
                Children.grandChildren = grandChild;
              });
            });
          });
        });
      return parents;
      })
        );

}

 getChildren(parent: Parent): Observable<Child[]> {
  return this.http.get<Children[]>(baseUrl + `parent/${parent.id}/children`, {withCredentials: true});

}

 getGrandChildren(child: Child): Observable<GrandChild[]> {
return this.http.get<GrandChild[]>(baseUrl + `children/${child.id}/GrandChildren`, {withCredentials: true});

}

4 Answers 4

1

Looking at your problem, it seems to be of a recursive nature.

Parent, Child, GrandChild are all similar and look like a flat representation of nodes and leafs of a tree. If this is the case, you may look at this for some details on how to do recursive processing with RxJS.

Remaining strictly to the way you represented your problem, at least up to my understanding, here is a way to solve it

getParents()
.pipe(
  switchMap(parents => from(parents)),
  mergeMap(parent => getChildren(parent)
    .pipe(
      switchMap(children => from(children)),
      mergeMap(child => getGrandChildren(child)
        .pipe(
          tap(grandChildren => child.grandChildren = grandChildren),
          map(() => child)
        )
      ),
      toArray(),
      tap(children => parent.children = children),
      map(() => parent)
    ), 1  // cuncurrency set to 1 to guarantee same order
  ),
)
.subscribe(d => console.log(JSON.stringify(d, null, 2)))

I have tested it with the following simulation data and functions

const parents = [
  {id: 1, name: 'name1'},
  {id: 2, name: 'name2'},
];
const children = [
  {childId: 1, name: 'name1'},
  {childId: 2, name: 'name2'},
  {childId: 3, name: 'name3'},
];
const grandChildren = [
  {grandChildId: 1, name: 'name1'},
  {grandChildId: 2, name: 'name2'},
  {grandChildId: 3, name: 'name3'},
];

function getParents(): Observable<Parent[]> {
  return of(parents).pipe(delay(10));
}
function getChildren(parent: Parent): Observable<Child[]> {
  return (parent.id === 1 ? of(children) : of([])).pipe(
    delay(100)
  );
}
function getGrandChildren(child: Child): Observable<GrandChild[]> {
  return (child.childId === 1 ? of(grandChildren) : of([])).pipe(delay(1000));
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks bro! It solved my problem. One small problem, data doesn't come in right order, but i can sort that!
I does not come in the right order since I have put some delays to make it more realistic, in case you have to fetch those data from a remote system via an async API. You can solve the problem substituting the first mergeMap with concatMap or adding 1 as concurrency parameter again in the first mergeMap. I have updated my response with the second option.
Yes, I figured it out ishould use concatMap, had to add toArray() at end too, cause it was returning single object. Thanks for help!!!
1

You can do something like this:

 getRoot() {
    return this.http.get<Parent[]>(baseUrl + 'parent', {withCredentials: true}).pipe(
      switchMap(items => forkJoin(items.map(i => this.getChild(i))))
    )
  }

  getChild(parent) {
    return this.http.get<Children[]>(baseUrl + `parent/${parent.id}/children`, {withCredentials: true}).pipe(
      switchMap(items => forkJoin(items.map(i => this.getGrandChild(i)))),
      map(resp => (parent.children = resp, parent))
    );

  }

  getGrandChild(parent) {
    return this.http.get<GrandChild[]>(baseUrl + `children/${child.id}/GrandChildren`, {withCredentials: true}).pipe(
      map(resp => (parent.grandChildren = resp, parent))
    )
  }

The uses items.map to convert the array of results into an array of requests for the relevant children.

It then uses forkJointo join all of those requests into 1 Observable

It then uses switchMap to make the requests, and map to add the children onto the response.

Here is a Stackblitz to demonstrate

Comments

0

is it possible that you confuse uppercase and lowercase? Use Conventions to be able to recognize something.

Well try parent.children

Comments

0

HttpClient will return Observable<any> data so you cannot just read the data in using mapoperator and you are trying to loop through the Observable<any>

To get the data from this function - this.getData() you need to access this from your component class like this.getData().subscribe((res) => {console.log(res)}) now you can see the data in the console

So now loop through the array and call the another service function - make sure you have separate function for all the parent children and grandChildren or else have a common http.get() function and pass down the URL to get the data - it is not a best way to have nested HttpClient calls

Hope you understood - happy coding

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.