Is there a way to specify the sequence in which Observables are subscribed to in Angular?

Working with API calls in a service.ts file has brought me some challenges. Here is the code snippet:

//code
getCars()
{
this.car = this.http.get(car_url)
return this.car;
}

getTires()
{
this.tires = this.http.get(tires_url)
return this.tires;
}

getSeats()
{
this.seats = this.http.get(seats_url)
return this.seats;
}

In the detail.compoenent.ts file, I need to filter data for a selected car and display it using detail.component.html. Here's a snippet of detail.compoenent.ts:

//code
ngOnInit() 
{
this.Service.getCars()
.subscribe(cars => this.car = cars.find(/*code (selecting car with certain car_id*/)

this.Service.getTires()
.subscribe(tires => {this.tires = tires.filter(/*code (selecting all the tires with the selected car_id*/)}

this.Service.getSeats()
.subscribe(seats => {this.seats = seats.filter(/*code (selecting all the seats with the selected car_id*/)}
}

To ensure that the filters for tires and seats are executed only after getCars(), how should I structure my code? I want getCar() to be called first because its information is needed for filtering tires and seats.

Answer №1

To chain multiple observables together in Angular, you can utilize the mergeMap() operator. This allows you to access data from one API call and use it in subsequent calls.

this.Service.getCars().pipe(mergeMap((cars) => {
    // Access cars data here
    return this.Service.getTires().pipe(mergeMap((tireData) => {
        // Access tireData here
        return this.Service.getSeats()
    }))
})).subscribe((seats) => {

})

You have the option to combine all three API calls within the first pipe by using mergeMap.

Answer №2

When dealing with nested subscriptions in your code, it often indicates a problem. Instead of using nested subscriptions, consider utilizing higher order observables like switchMap, concatMap, mergeMap, zip, and others.

To help you better understand the concept, I have created a comprehensive demonstration of the desired outcome.

Let's begin by defining the necessary interfaces for improved type safety:

export interface Car {
  id: string;
  seatId: string;
  tyreId: string;
}

export type CarResolved = Omit<Car, "seatId" | "tyreId"> & {
  seat: Seat;
  tyre: Tyre;
};

export interface Tyre {
  id: string;
  diameter: number;
}

export interface Seat {
  id: string;
  width: number;
}

With our data structure defined, we can now create a service that provides mock data which can later be replaced with actual backend data:

@Injectable()
export class ResourcesService {
  public getCars(): Observable<CarResolved[]> {
    // Implementation details omitted for brevity
  }

  private _getCars(): Observable<Car[]> {
    // Implementation details omitted for brevity
  }

  private _getTyreById(id: string): Observable<Tyre> {
    // Implementation details omitted for brevity
  }

  private _getSeatById(id: string): Observable<Seat> {
    // Implementation details omitted for brevity
  }
}

// Mock data for demonstration purposes
const mockCars: Car[] = [
  { id: "car-1", seatId: "seat-1", tyreId: "tyre-1" },
  { id: "car-2", seatId: "seat-2", tyreId: "tyre-2" },
  { id: "car-3", seatId: "seat-1", tyreId: "tyre-3" }
];

const mockTyres: Tyre[] = [
  { id: "tyre-1", diameter: 80 },
  { id: "tyre-2", diameter: 60 },
  { id: "tyre-3", diameter: 75 }
];

const mockSeats: Seat[] = [
  { id: "seat-1", width: 10 },
  { id: "seat-2", width: 20 },
  { id: "seat-3", width: 30 }
];

The key point to note is that the `getCars` method does not involve any subscription. It simply returns an observable that can be subscribed to later on. Everything is managed through streams.

Lastly, let's look at the view setup:

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  public cars$: Observable<CarResolved[]> = this.resourcesService.getCars();

  constructor(private resourcesService: ResourcesService) {}
}

Similar to the service, there are no subscriptions involved in accessing the data in the component!

Here's a preview of how the data is displayed in the HTML:

<pre>{{ cars$ | async | json }}</pre>

The updated view presents the resolved car objects with all associated details.

For a live demo showcasing these concepts, visit stackblitz: https://stackblitz.com/edit/angular-qypary

Answer №3

A straightforward approach would involve executing subsequent http requests within callback functions.

ngOnInit() 
{
    this.Service.getCars().subscribe(
      cars => {
           this.car = cars.find();
           this.Service.getTires().subscribe(tires => {
              this.tires = tires.filter();
              this.Service.getSeats().subscribe(
                seats => {this.seats = 
              seats.filter(/*code (selecting all 
                 the seats with the selected car_id)*/}
            }
         }

      });

}

To keep the end user informed while data is loading, you can display a spinner.

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

determine the values of objects based on their corresponding keys

Still on the hunt for a solution to this, but haven't found an exact match yet. I've been grappling with the following code snippet: interface RowData { firstName: string; lastName: string; age: number; participate: boolean; } c ...

Using nginx reverse proxy to serve an Angular application hosted on a nginx server

My angular app is running in a docker container on nginx named "website". I am trying to access this container from another nginx reverse-proxy container called "test.nginx", but I'm unsure how to configure the nginx.conf for reverse-proxy. First, I ...

What is the best way to generate a random item when a button is clicked?

I'm currently working on a feature in my component that generates a random item each time I access the designated page. While the functionality is set to automatically refresh and showcase a new random item, I am now looking to trigger this action man ...

The test failed to execute due to disconnection (0 occurrences) as no message was received within the 30000 ms timeframe

Currently, I am facing an issue with my Angular application. When I execute the "ng test" command, it displays an error message stating 'Disconnected (0 times), because no message in 30000 ms.' I have tried updating both karma and jasmine package ...

Exploring the world of TypeScript type mappings

I'm currently working on enhancing a function with type annotations. This particular function takes an array of typed objects as parameters and returns a mapped array of a different type: const createAnimals = <T extends AnimalFactory<any>[]& ...

Puppeteer with Typescript: Encountering issues during the transpilation process

The issue stems from the fact that I am unable to use Javascript directly due to Firebase Functions Node.JS version lacking support for Async/Await. As a workaround, I have converted the code into Typescript and am currently attempting to transpile it to c ...

Despite the unconsumedBufferLength being 0, DataReader.loadAsync is still being completed

Working on UWP WinRT, I'm dealing with JSON stream consumption using the following code: async function connect() { let stream: MSStream; return new CancellableContext<void>( async (context) => { stream ...

What is the best method to completely uninstall Apollo-Angular along with all of its dependencies?

Once I added apollo-angular and @apollo/client to my project, I quickly realized that I no longer needed them. However, simply using "npm uninstall apollo-angular" and "npm uninstall @apollo/client" only removed the main folders but left behind other Apoll ...

Endpoint not returning data as expected

I'm facing an issue with my routing module where I have successfully used activatedRoute for multiple other modules but now running into trouble when implementing it in a new singular component. The structure of my routing module is as follows: const ...

What is the best way to assign JSON data to a Class variable within Angular?

In my code, I have a class called Projects export class Projects { project_id: number; project_name: string; category_id: number; project_type: string; start_date: Date; completion_date: Date; working_status: string; project_info: string; area: string; add ...

Encountering a mysterious server error after configuring CORS settings accurately as directed in the Microsoft documentation

Previously, I was encountering a 405 Method Not Allowed error, but now it seems to have transitioned into a 0 Unknown Error after configuring CORS for both the controller and start.cs. My current focus is on the Error_connection_refused. Why is it being re ...

Combining ngModel and ngClass in Angular: a comprehensive guide

I have implemented the following code in Angular 6 using Visual Studio Code <div [ngClass]="{'disabled': isReadOnly}"> <label class="switch"> <input type="checkbox" name="Gender" ...

Tips on implementing computed properties in Vue.js while using TypeScript

There is a significant amount of documentation on how to utilize Vue.js with JavaScript, but very little information on using TypeScript. The question arises: how do you create computed properties in a vue component when working with TypeScript? According ...

Displaying data in a table using NgFor and allowing the user to input the number of columns

My component has the capability to accept an array input, such as [1, 2, 3, 4, 5, 6, 7], and a number of columns input. For example, if a user specifies 3 columns, I want to display the data in a table with 3 columns like this: 1 2 3 4 5 6 7 If the ...

Locate the minimum and maximum values between two inputted dates

I'm looking for a solution that provides strongly typed code. The problem arises when trying to implement solutions from a related question - Min/Max of dates in an array? - as it results in an error. TS2345: Argument of type 'Date' is not ...

Bidirectional binding with complex objects

In my Angular2 app, I have a class called MyClass with the following structure: export class MyClass { name: Object; } The name object is used to load the current language dynamically. Currently, for two-way binding, I am initializing it like this: it ...

What could be causing IE11 to cut off my JavaScript file prematurely?

Edit: I believe I have resolved the issue, and it seems to be more related to Angular than I initially thought. The problem was caused by a fat arrow in the script, which was not easily visible due to code minification. This script is a global script that ...

Error Message in Angular 6 When Importing JQVMap - No Such 'vectorMap' Property on Type 'JQuery<HTMLElement>'

I'm currently facing some challenges trying to integrate a clickable map, JQVMap and JQuery into my Angular 6 project. Issue: error TS2339: Property 'vectorMap' does not exist on type 'JQuery'. Here is the component in question: ...

developed a website utilizing ASP MVC in combination with Angular 2 framework

When it comes to developing the front end, I prefer using Angular 2. For the back end, I stick with Asp MVC (not ASP CORE)... In a typical Asp MVC application, these are the steps usually taken to publish the app: Begin by right-clicking on the project ...

Execute tap() function without subscribing

Can the tap() pipe function be executed without subscribing to it? observer$:BehaviorSubject<number[]> = new BehaviorSubject<number[]>([1]) getData(page:number):void{ of(page).pipe( tap({ next: (data) => this.observe ...