Tips for optimizing HttpRequests within nested for-loops that utilize subscribe()?

Our REST-API is designed to be frontend agnostic, meaning it always sends the IRI to nested resources. This results in multiple HTTP calls needed to retrieve data; first for the parent resource, then its child resources and so on. Each Country has linked Entries, with each entry connected to a product that has an IRI to its category resource.

export interface Country {
  countryId: number;
  name: string;
  code: string;
  projects: string[]; //contains an array of IRIs for the project resources
  entries: string[];
}

export interface Entry {
  entryId: number,
  country: string,
  information: string,
  strategy: string,
  action: string,
  user: string,
  product: string,
  salesStatus1: string,
  salesStatus2: string,
  salesStatus3: string,
  salesStatus4: string,
  salesStatus5: string,

}

export interface Product {
  productId: number,
  productName: string,
  sharepointId: string,
  category: string,
  plmId: string,
  productStatus1: string,
  productStatus2: string,
  productStatus3: string,
  productStatus4: string,
  productStatus5: string,
  productComponents: string[]
}

export interface Category {
  categoryId: number,
  name: string,
  children: string[]
}
export class Node {
  children: Node[] = [];
  name: string;
  isProduct: boolean;
}

To construct a navigation tree displaying all required data, I wrote the following code:

ngOnInit() {
    let nodes: Node[] = new Array<Node>();
    this.countriesService.getAll()
      .subscribe(
        (countries) => {
          for (let country of countries) {
            let countryNode = new Node();
            countryNode.name = country.name;
            countryNode.isProduct = false;
            for (let entryUrl of country.entries) {
              this.entriesService.getByUrl(entryUrl).subscribe(
                (entry) => {
                  this.productsService.getByUrl(entry.product).subscribe(
                    (product) => {
                      this.categoryService.getByUrl(product.category).subscribe(
                        (category) => {
                          let categoryNode = new Node();
                          categoryNode.name = category.name;
                          categoryNode.isProduct = true;
                          countryNode.children.push(categoryNode);
                          for (let childrenUrl of category.children) {
                            this.categoryService.getByUrl(childrenUrl).subscribe(
                              (childCategory) => {
                                let categoryChildNode = new Node();
                                categoryChildNode.name = childCategory.name;
                                categoryChildNode.isProduct = false;
                                categoryNode.children.push(categoryChildNode);
                              }
                            )
                          }
                        }
                      )
                    }
                  )
                }
              )
            }
            nodes.push(countryNode);
          }
          this.dataChange.next(nodes);
        }
      );
  }

However, being new to Angular and RxJS, I am facing challenges ensuring all asynchronous calls are completed before constructing the navigation tree. The current chaining approach feels inefficient and messy. I'm seeking guidance on refactoring the code using better RxJS methods but unsure where to start, especially when handling nested resource IRIs and creating nodes for tree navigation.

Any assistance on how to refactor this code would be greatly appreciated.

Answer №1

Exploring a unique approach, I integrated the requests using rxjs operators.

this.countryService.retrieveAll()
  .pipe(
    map((cs) => forkJoin(cs.map(c => {
      const countryNode = new GraphNode();
      countryNode.name = c.name;
      countryNode.isProduct = false;

      return forkJoin(c.entries.map(entryUrl => this.entryService.fetchByUrl(entryUrl)))
        .pipe(
          switchMap(entries => forkJoin(entries.map(entry => this.productService.fetchByUrl(entry.product)))),
          switchMap(products => forkJoin(products.map(product => this.categoryService.fetchByUrl(product.category)))),
          switchMap(categories => forkJoin(categories.map(category => {
            const categoryNode = new GraphNode();
            categoryNode.name = category.name;
            categoryNode.isProduct = true;

            return forkJoin(category.children.map(childrenUrl => this.categoryService.fetchByUrl(childrenUrl)))
              .pipe(
                map(cc => {
                  categoryNode.children = cc.map(childCategory => {
                    const categoryChildNode = new GraphNode();
                    categoryChildNode.name = childCategory.name;
                    categoryChildNode.isProduct = false;
                    return categoryChildNode;
                  });
                  return categoryNode;
                })
              );
          }))),
          map(categoryNodes => {
            countryNode.children = categoryNodes;
            return countryNode;
          })
        );
    })))
  ).subscribe(countryNodes => {
    this.updateData.next(countryNodes)
  });

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the reason behind the ability to assign any single parameter function to the type `(val: never) => void` in TypeScript?

Take a look at the code snippet below interface Fn { (val: never): void } const fn1: Fn = () => {} const fn2: Fn = (val: number) => {} const fn3: Fn = (val: { canBeAnyThing: string }) => {} Despite the lack of errors, I find it puzzling. For ...

Navigating an Array in Typescript

Angular is linked to node.js, which interacts with mongodb to fetch data successfully. However, I am now faced with the challenge of mapping variables in my typescript component to the backend node.js. When viewing the data structure in the browser consol ...

Displaying time in weekly view on the Angular 4.0 calendar

I've integrated the library into my Angular application to manage calendar events display and creation. The app features a monthly, weekly, and daily view option for users. However, I noticed that in the weekly view, only the dates are shown without ...

What is the method for retrieving an attribute's value from an object that does not have key-value pairs?

My current project involves working with dynamoose and running a query that produces the following output: [ Document { cost: 100 }, lastKey: undefined, count: 1, queriedCount: undefined, timesQueried: 1 ] When I use typeof(output), it returns O ...

Tips for preserving images while browsing a website built with Angular single-page application

Utilizing Angular's router for component navigation has been beneficial, but I am facing an issue with component reloads when going back. To address the problem of content reloading from the server, I have implemented a solution where the content arra ...

Invoking a self-executing anonymous function using requestAnimationFrame

Recently, I developed a basic 2D-tile-renderer using JavaScript and decided to convert it to TypeScript. The process went smoothly, with the only challenge being when I tried to call window.requestAnimationFrame with a callback function. Eventually, I was ...

Conditional application of Angular animations is possible

After implementing the fadein effect from Angular-Animations in my ASP.NET based Angular project, I encountered an issue where only the first row is faded-in while the other rows are not displayed when using *ngIf. Here is a snippet of the code: <ng-te ...

Guide on showing a component exclusively for iPads with React and TypeScript

I need help displaying an icon only in the component for iPad devices, and not on other devices. As a beginner in coding for iPads and mobile devices, I am unsure how to achieve this specific requirement for the iPad device. Below is the code snippet tha ...

Importing Angular Material modules

I've integrated the Angular Material module into my project by updating the material.module.ts file with the following imports: import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MatT ...

Utilize Knex to retrieve data from the req.query

express and knex have been giving me some trouble; I am struggling to get this endpoint working using req.querys (response from express), even though it worked fine when I used req.params. Express: app.get(`/actor`, async (req: Request, res: Response) =&g ...

ERROR: PipeError - Conversion of "Invalid Date" to a date is not possible for the DatePipe

While attempting to format a date with time, I ran into an error. The way I am sending the request is as follows: created = this.datePipe.transform(dateCreated, 'yyyy-MM-ddTHH:mm'); I require the time in order to consume a service that necessi ...

Encountering an issue while trying to execute the command "ionic cordova build android --prod --release

Currently, I am facing an issue while trying to build my apk for deployment on the Play Store. The error message is causing a time constraint and I urgently need to resolve it. Any help or suggestions regarding this matter would be greatly appreciated. ...

Retrieving the component's values when utilizing the `<ng-content>` directive

Seeking a technique for accessing the values of a component when utilizing <ng-content>: import {Component} from '@angular/core'; @Component({ selector: 'home-page', template: `<person-box>{{name}}</person-box> & ...

Nextjs 14 experiences full page loading due to the presence of multiple root layouts

The issue I'm facing involves a full page load when navigating between two root layout pages In my Next.js application (NextJS 14), I have created two root layouts. However, when moving from the first layout to the second layout, it triggers a comple ...

Challenges in handling asynchronous data within static JSON objects in Angular2

I have a service set up with some static objects that are being utilized in my UI. fetchRulesVariables() fetchRuleVariables() { let variables = [ { name: 'Credit Funding Type', id: 1, multiple: ...

Running a Jest test that triggers process.exit within an eternal setInterval loop with a latency of 0, all while utilizing Single

In the original project, there is a class that performs long-running tasks in separate processes on servers. These processes occasionally receive 'SIGINT' signals to stop, and I need to persist the state when this happens. To achieve this, I wrap ...

Ways to integrate user input into the header of an Angular HTTP post method

I need help figuring out how to incorporate user input into the header of a post method I am working with. I understand that some kind of binding is necessary, but I'm struggling to implement it in this case. Currently, I have a variable called postDa ...

Navigating Between Pages with Parameters in Ionic 2 (starter app)

I have an Ionic 2 project with a blank template containing a page that displays a list. Upon clicking on an item in the list, the user should be able to view more details about that specific item. Below are the files related to the list: list.html: <i ...

What is the reason behind being unable to register two components with the same name using React Hook Form?

I have encountered an issue while using the useForm hook from React Hook Form library. Due to the specific UI library I am using, I had to create custom radio buttons. The problem arises when I try to register two components with the same name in the form ...

Angular 11 now includes the ability to implement lazy loading for modules

Here is the configuration of my app-routing.module.ts: const routes: Routes = [ { path: 'login', component: LoginComponent }, { path: '', canActivate: [AuthGuard], component: HomeComponent, children ...