What are some effective strategies for utilizing observables for handling http requests in an Angular application?

In my Angular application, I am retrieving data from APIs. Initially, my code in detail.component.ts looked like this:

//code
getData() {
  this.http.get(url1).subscribe(data1 => {
    /* code: apply certain filter to get a filtered array out */

    this.http.get(url2).subscribe(data2 => {
      /* code: apply certain filter to get a filtered array out */

      this.http.get(url3).subscribe(data3 => {
        /* code: apply certain filter to get a filtered array out */
      }) // closing third subscribe form

    }) // closing second subscribe form

  }) // closing first subscribe form
}

As the number of calls increased, nesting them inside each other made the code messy. After some research, I discovered that using observables could help solve this issue. I refactored the code and now it looks like this in data.service.ts:

//code
getData1() { 
  this.data1 = this.http.get(this.url1)
  return this.data1;
}

getData2() {
  this.data2 = this.http.get(this.url2)
  return this.data2;
}

getData3() {
  this.data3 = this.http.get(this.url3)
  return this.data3;
}

In detail.component.ts:

//code
ngOnInit() {
  this.dataService.getData1().subscribe(data1 => {
    /* code: apply certain filter to get a filtered array out */

    this.dataService.getData2().subscribe(data2 => {
      /* code: apply certain filter to get a filtered array out */

      this.dataService.getData3().subscribe(data3 => {
        /* code: apply certain filter to get a filtered array out */
      }) // closing third subscribe form

    }) // closing second subscribe form

  }) // closing first subscribe form
}

Data1 must be executed first as Data2 and Data3 require information from the filtered Array obtained by Data1. This is why implementing solutions like forkJoin has been challenging. Is there a better approach to tidy up the code and maintain its functionality?

Answer №1

Observable provides a plethora of methods and tools to create elegant and easy-to-read pipelines. Let's take a look at an example:

// my.service.ts
getData1(): Observable {
  return this.httpClient.get();
}

getData2(): Observable {
  return this.httpClient.post();
}

getData3(): Observable {
  return this.httpClient.get();
}
my.component.ts

ngOnInit() {
  this.myService.getData1().pipe(
    map(data => {
      // manipulate the request response as needed
      data.id += 1;
      return data;
    }),
    switchMap(data => this.myService.getData2(data)),
    delay(2000),
    switchMap(data => this.myService.getData3(data)),
  ).subscribe(
    data => console.log(data); // Final result of the pipeline
  )
}

Explanation of the process:

  1. getData1() is called, returning an Observable containing our HttpRequest
  2. We modify the response from the previous request (increment the id)
  3. The modified response is used to call getData2, which returns an Observable of the new request
  4. A 2000ms delay is introduced before proceeding
  5. The response from getData2 is passed to getData3 for another request
  6. We subscribe to the pipeline to obtain the final result

If you don't subscribe to a pipeline, it won't execute

RxJs is a powerful library for working with data, but many people struggle to utilize its full potential.

To effectively use RxJs, remember just one rule:

  • Avoid nesting Observables

Additional resources for further exploration (Credits to @LingVu):

  • How to do the chain sequence in rxjs

  • RxJS Promise Composition (passing data)

  • RxJS sequence equivalent to promise.then()?

Answer №2

Utilize the switchMap operator to toggle between different observables...

this.userService.getInfo1().pipe(
  switchMap(info1 => {
    // perform operations with info1
    return this.userService.getInfo2()
  }),
  tap(info2 => {
    // carry out actions with info2
    return this.userService.getInfo3()
  })
).subscribe()

Answer №3

If you're looking to improve performance, consider using the async await method

async fetchInitialData() {
    const data1 = await this.dataService.getData1().toPromise();
    // apply specific filter for data manipulation

    const data2 = await this.dataService.getData2().toPromise();
    // apply another filter for data processing

    const data3 = await this.dataService.getData3().toPromise();
    // apply additional filters as needed
}

An alternative approach could be using merge map

this.dataService.getData1()
.pipe(
  map((data1) => {
    // implement filtering logic here
    return data1;
  }),
  mergeMap((data1) => {
    this.dataService.getData2();
  })
)
.subscribe((finalData) => {
});

Or try out concatMap for a different handling

this.dataService.getData1()
  .pipe(
    tap(data1 => console.log(data1)),
    concatMap((data1) => {
      this.dataService.getData2(data1);
    }),
    tap(data2 => console.log(data2)),
    concatMap((data2) => {
      this.dataService.getData3(data2);
    }),
    tap(data3 => console.log(data3)),
  )
  .subscribe(result => {
    console.log(result)
  });

Answer №4

When it comes to chaining Rxjs, you may find that many people have asked similar questions before. Here are some previous inquiries on SO that you can check out:

  • How to create a chain sequence in rxjs

  • Exploring RxJS Promise Composition (passing data)

  • Looking for an RxJS sequence similar to promise.then()?

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

Setting a border on a specific column in ag-grid is a simple task that can help you customize

I have a table where the first set of columns differs from the rest. I am looking to emphasize this distinction by adding a vertical border on the right side of the specific column to highlight it both in the header and rows. Our setup includes using the ...

Firestore - Insert new entries into an array in an existing document using Typescript

After retrieving data from Firestore using Firebase and implementing pagination function, I encounter an issue when attempting to add the new values to an array. Specifically, TypeScript raises an error message when I try to invoke the setState hook. int ...

When I attempt to return an object from a function and pass the reference to a prop, TypeScript throws an error. However, the error does not occur if the object is directly placed in

Currently, I have the following code block: const getDataForChart = () => { const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July']; const test = { ...

The return type from the RxJS Observable reduce method may differ from the type initially provided

Dealing with Angular4, TypeScript, and RxJS 5 I am encountering an issue where I have a complex type being passed into a reduce method, but I want the return type to be a simple boolean value. Currently, the following code is giving me a return type erro ...

Exploring Angular2: The Router Event NavigationCancel occurring prior to the resolution of the Route Guard

My application has routes protected by an AuthGuard that implements CanActivate. This guard first checks if the user is logged in and then verifies if certain configuration variables are set before allowing access to the route. If the user is authenticated ...

When the browser is refreshed in Angular, the default root component will show up instead of the one specified in routes

I'm facing an issue with browser refresh on my Angular application. Every time I reload the page, either by refreshing the browser or entering a URL, the app redirects to the "/" route. Despite trying various solutions, none seemed to resolve the iss ...

Using ion-icon inside a clickable ion-card while applying float: right does not render properly

I am facing an issue with a list of ion-cards that have clickable icons appearing when you hover over them. The problem is that due to the floating nature of the icons, they are not positioned correctly (as shown in the image below) and end up getting cove ...

Utilize your access token to send a message through Google Business Messages

Currently, I have successfully set up a method to send messages using the Google Business Messages API from an agent to a user through NodeJS. const bmApi = new businessmessages.businessmessages_v1.Businessmessages({}); This process requires authenticatio ...

Testing the subscribe function in Angular within a returned Promise: A guide

I am facing an issue with a service that returns a Promise. It retrieves data from a JSON file using a subscribe method before resolving the Promise. I am trying to test the result of this Promise based on the parameters I provide, but I am encountering t ...

Exploring nested grandchildren components in Angular2 Dart

Currently, I am attempting to make the following code work in Angular2 Dart: <div class='test'> <accordion2> <accordion-item-2 title="blah" active="true"> <sample-form-1></sample ...

Guide on converting any object with keys of type string to a generic type

I'm grappling with a function that yields an Output generic type. In this function, I initiate an API request that responds with a json object. My aim is to have the function return this json object in the Output Generic type format Take a look at th ...

Leverage bespoke webpack configuration in an Angular project

I needed to implement webpack in my Angular application, so I included the following code. package.json file "scripts": { "start_ar": "ng build --watch --configuration=dev_ar", }, "devDependencies": { "@angular-builders/custom-webpack": "^7.1.4", "file ...

Arrange the items that are missing from Array B to be located at the bottom of Array A, organized in a file tree structure

I have two arrays containing different types of objects. Each object in the arrays has a title assigned to it. My goal is to compare these two arrays based on their titles and move any files that are not included in the bottom part of the fileStructure arr ...

The content of the string within the .ts file sourced from an external JSON document

I'm feeling a bit disoriented about this topic. Is it feasible to insert a string from an external JSON file into the .ts file? I aim to display the URLs of each item in an IONIC InAppBrowser. For this reason, I intend to generate a variable with a sp ...

Error: In Angular Firebase, the type 'string' cannot be assigned to the type 'Date'

I am encountering an error. The following error is shown: "cannot read property 'toDate' of undefined. Without the toDate() | Date." After further investigation, I found: A Timestamp object with seconds=1545109200 and nanoseconds=0. A hel ...

Having trouble with unit testing a subscription within the ngOnInit lifecycle hook

I subscribed to an observable in the ngOnInit method, but when I try to write a test for it, it doesn't seem to be working. Below is the class I am attempting to test: ngOnInit() { this.myService.updates$.pipe(takeUntil(unsusbribe$)) .subscribe ...

What is the best way to automatically close a parent popup window when the child popup is

Currently, I am developing a web application using Angular that requires managing nested popups. Specifically, in my scenario, I initiate Popup 1 and from within that popup, I trigger another popup (Popup 2) upon clicking "delete." My goal is to ensure tha ...

Issue with Angular 6: Textarea displaying value as Object Object

I have data saved in local storage using JSON.stringify, and I want to display it in a textarea. Here are the relevant code snippets: { "name": "some name" } To retrieve the data, I'm using this: this.mydata = localStorage.getItem('mydata&a ...

The field 'name' is not recognized on type 'never'

Currently immersing myself in Angular and experimenting with making an API call, but I'm encountering the following error: error TS2339: Property 'name' does not exist on type 'never'. import { Component, OnInit } from '@angu ...

Adding a new value to a string variable using TypeScript

Just starting out with Angular and I'm looking to capture user input from a text box with each keystroke and add it to a string variable like in Java. In my text box, I have the following setup for capturing key events: <input type="text" ...