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

Discover the power of sharing a service instance in Angular 2 RC5

In the past, I shared a service instance by declaring it as a viewInjectors within my @Component like so: @Component({ selector: 'my-sel', viewInjectors: [SharedService], templateUrl: 'template.html', pipes: [MyPipe] }) ...

Implementing handleRequest as an asynchronous function within the passportjs guard

@Injectable() export class RefreshAuthGuard extends JwtAuthGuard { constructor( private readonly jwtService: JwtService, ) { super(); } public handleRequest(err: any, user: any, info: Error, ctx: any): any { if (err ...

Having trouble correctly displaying a form with nested form array within a form group

I am working with a form group that contains nested form groups and a form array: ngOnInit() { this.form = this.fb.group({ dropDownOptions: this.fb.group({ items: this.fb.array([this.createItem()]) }) ...

Is there stability in using *ngFor for lists in Nativescript Angular?

Update: I have inquired about the current status of RadListView in Nativescript, questioning if it still exists. You can find more information here. Initial query: Is it suitable to utilize *ngFor for lists in Nativescript? Typically, I see recommendatio ...

How to update nested properties in Typescript using bracket notation

Imagine there is an interface and object with nested properties as shown below: interface Iobj { a: { a2:string }; b: string; } const obj: Iobj = { a:{ a2: "hello" } b: "world" }; Now let's say we have strings that ...

Sending data using formData across multiple levels of a model in Angular

I have a model that I need to fill with data and send it to the server : export interface AddAlbumeModel { name: string; gener: string; signer: string; albumeProfile:any; albumPoster:any; tracks:TrackMode ...

What is the equivalent of a "Class" in Typescript for defining an "Interface"?

I am interested in passing "Interfaces" to a function. Not just a specific interface, but any interfaces. As explained here, for Class, I can handle it as a type. export type ClassType<T> = { new(...args: any[]): T }; function doSomethingWithAnyCla ...

Having trouble accessing undefined properties? Facing issues with the latest Angular version?

Why am I encountering an error and what steps can be taken to resolve it? Currently using the latest version of Angular. ERROR TypeError: Cannot read properties of undefined (reading 'id') Here is the JSON data: { "settings": [ { ...

Is there a mistake in the TypeScript guide for custom typography in MUI5?

Currently, I am in the process of setting up custom typography variants in MUI5 by referencing this helpful guide: https://mui.com/customization/typography/#adding-amp-disabling-variants. As I follow step 2 and input the type definitions: declare module &a ...

retrieve Angular data across components using Input

When using fetch to make a request to the reqres api users in app.component, I then share the data with its child component (hello.component) via Input. While I am able to get the correct user names in the child template, I encounter an issue when trying t ...

Is it feasible to send a variable to angular.json in order to prevent repetitive code?

I'm searching for a strategy to efficiently pass data to the angular.json file, eliminating the need for redundant build configurations. To illustrate my point better, let's use an example. Currently, in my angular.json file under the configurati ...

Creating a Docker Image for Node.Js Using Bazel

Reason Behind the Need I am diving into the Bazel world and struggling to find comprehensive references on constructing Docker images for Node.js. My focus lies on a Typescript-based Node.js application that relies on two other Typescript packages. My ul ...

Discovering the data types for node.js imports

When starting a node.js project with express, the code typically begins like this - import express = require('express') const app = express() If I want to pass the variable 'app' as a parameter in typescript, what would be the appropri ...

What is the best way to make the current year the default selection in my Select control within Reactive Forms?

Hey there! I managed to create a select element that displays the current year, 5 years from the past, and 3 years from the future. But now I need to figure out how to make the current year the default selection using Reactive Forms. Any ideas on how to ac ...

Incorporating a counter feature into an Angular HTML document

In this section, I am displaying the restaurants that are filtered based on their name and address. If no name or address is provided, all restaurants are shown. However, I am facing an issue as I need to incorporate a counter to keep track of the remainin ...

When a child triggers a non-bubbling event, the view reference in Angular 2 structural directive is automatically cleared

Imagine we have a set of images that need to be displayed: <div *ngFor="let image of images; let i = index"> <div *appMaskImageOnError="i" #mydir> <img [src]="image" alt="" (error)="mydir.remove()"> </div> < ...

Using Typescript, Angular, and Rxjs to retrieve multiple HttpClients

I am looking to send get requests to multiple endpoints simultaneously, but I want to collect all the responses at once. Currently, this is how a single endpoint request is handled: public getTasks(): Observable<any> { this.logger.info('Ta ...

Angular 2 experiencing issues with the authorization header

Hello there! I am currently working with the Ionic 2 framework alongside Angular, and I'm facing an issue while trying to make an HTTP request with the authorization header. It seems like the header is not being sent properly. Can someone help me iden ...

Synchronizing Form Data in Angular 5: Pass and Populate Dropdowns between Components

I have developed a unique form (material dialog modal) that allows users to create an account. When the user clicks on the Register button, their created account should appear in a dropdown menu without redirecting or reloading the current page. I am facin ...

I'm looking to inject both default static values and dynamic values into React's useForm hook. But I'm running into a TypeScript type error

Below is the useForm code I am using: const { register, handleSubmit, formState: { errors, isSubmitting }, reset, getValues, } = useForm({ defaultValues: { celebUid, //props fanUid, // props price, // props ...