Attempting to combine numerous observables into a single entity within an Angular 2 project

I am grappling with the concept of Observables in RxJs. My task involves displaying all users for a specific site on a page. The User and SiteUser entities are located in separate API endpoints. Here are the relevant endpoints:

userService.getSiteUsers(siteId: string): Observable<SiteUser[]>;

where

export class SiteUser {
    site_id: string;
    user_id: string;
}

and

userService.getUser(user_id: string): Observable<User>;

where

export class User {
    id: string;
    name: string;
    email: string;
    ....
}

To accomplish this, I need to:

  1. Retrieve all user ids for a specific site using the siteUsers API
  2. For each user id, fetch user details using the getUser API call

This can be done as follows:

let users: User[] = []; // bound in html view to display table
this.userService.getSiteUsers("my site id")
                .subscribe((siteUsers) => {
                    for (let siteUser of siteUsers) {
                        this.userService.getUser(siteUser.user_id)
                                        .subscribe((user) => {
                                            users.push(user);
                                        });
                    }
                });

However, I feel that this approach is not optimal. There must be a better way to handle it using Observables. While I am new to Observables, my understanding suggests that there should be a cleaner approach. I tried the following idea but couldn't get it to work:

A potential solution could involve:

this.userService.getSiteUsers("my site id") 
                .selectMany((siteUser) => this.userService.getUser(user))
                .mergeAll()
                .subscribe((users) => {
                    this.users = users; 
                });

If anyone has suggestions or tips on how to improve this code, please share as I am struggling to implement it.

EDIT------

Possibly something along these lines:

this.userService.getSiteUsers("my site id")
    .switchMap(
       (siteUsers) => {
         let userQueries: Observable<User>[] = [];
         for (let siteUser of siteUsers) {
            userQueries.push(this.userService.getUser(siteUser.user_id));
         }

         return Observable.forkJoin(userQueries);
       }
    )
    .subscribe((users) => {
        this.users = users;
    });

Answer №1

When you have one http call that depends on another http call, it's recommended to use the .flatMap() / .mergeMap() operator.

For instance, a solution for your situation could look like this:

this.userService.getSiteUsers("my site id")
.switchMap(
   (siteUsers) => {
     let userQueries: Observable<User>[] = [];
     for (let siteUser of siteUsers) {
        userQueries.push(this.userService.getUser(siteUser.user_id));
     }

     return Observable.forkJoin(userQueries);
   }
)
.subscribe((users) => {
    this.users = users;
});

Answer №2

Here's an example to try out:

this.userService.getSiteUsers("my site id")
  .flatMap((siteUsers) => {
    // Convert each user into an array of observable requests
    const usersObservables = siteUsers.map(siteUser => this.userService.getUser(siteUser.user_id)).map((res:Response) => res.json())
    return Observable.forkJoin(...usersObservables)
  }).subscribe(users => {
      // All users are now available;
      console.log(users)
  });

We utilize the spread operator in this code snippet:

return Observable.forkJoin(...usersObservables)

This way, we change our array into arguments, as shown here:

return Observable.forkJoin(observableUser1, observableUser2, observableUser...)

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

Contrasting input: [] with the @Input() directive

Recently, I've begun exploring the world of child and parent component communication in Angular 4. In my research, I stumbled upon older videos that utilize the input: [] syntax instead of the now more prevalent @Input() syntax. Is there any distincti ...

Accessing property values from a map in Angular

Is there a way to retrieve a property from a map and display it in a table using Angular? I keep getting [object Object] when I try to display it. Even using property.first doesn't show anything. //model export interface UserModel { room: Map ...

Restricting the number of lines within a paragraph in Angular 2

Is there a method to limit the number of lines in a <p> tag and add an ellipsis (...) at the end? Using character count for truncation doesn't work well as the width of the element varies according to device screen size. ...

Consistentize Column Titles in Uploaded Excel Spreadsheet

I have a friend who takes customer orders, and these customers are required to submit an excel sheet with specific fields such as item, description, brand, quantity, etc. However, the challenge arises when these sheets do not consistently use the same colu ...

How can we transfer or exclude all boolean properties from one class to another or a "type"?

Within my Nestjs application, there is an entity class structured like this: @ObjectType() export class Companies { @Field(() => Int) @PrimaryGeneratedColumn({ type: 'int', name: 'id' }) public id: number; @Field() @Column ...

Is it feasible to add to an ID using ngx-bootstrap's dropdown feature?

In the documentation for ngx dropdown, there is a feature called "append to body." I recently tried changing this to append to a table element instead and it worked successfully. Now, on another page, I have two tables displayed. If I were to assign each ...

HTML elements are failing to display when utilizing the Angular selector

I'm currently working on a project with Angular 2/4 and I'm running into an issue with rendering the app.component.html in the main index.cshtml page. All I can see is the word "loading" that is hard-coded in the index.cshtml file. I've doub ...

Adding elements from one array to another array of a different type while also including an additional element (JavaScript/TypeScript)

I'm having trouble manipulating arrays of different types, specifically when working with interfaces. It's a simple issue, but I could use some help. Here are the two interfaces I'm using: export interface Group { gId: number; gName: st ...

Error encountered: React Typescript does not support the "any" type in a template literal expression

I have been encountering an issue with my slider component in React Typescript. The error message I keep getting is related to the "Invalid type 'any' of template literal expression" specifically at the const fillWidth variable. I am struggling t ...

Is it possible to apply a formatting filter or pipe dynamically within an *ngFor loop in Angular (versions 2 and 4

Here is the data Object within my component sampleData=[ { "value": "sample value with no formatter", "formatter": null, }, { "value": "1234.5678", "formatter": "number:'3.5-5'", }, { "value": "1.3495", "formatt ...

Unable to get the onchange event to trigger for a span element

Is there a way to trigger the onchange event on a span element that doesn't seem to be working? Here is the code I am using: Attempt 1 document.getElementById(seconds).addEventListener('change', (event: MutationEvent & { path: any }) =& ...

Angular Nested Interface is a concept that involves defining an

Looking for guidance on creating a nested interface for JSON data like this: Any help is appreciated. JSON Structure "toto": { "toto1": [], "toto2": [], "toto3": [], } Interface Definition export interface Itot ...

Why is it considered an error when an index signature is missing in a type?

Consider the TypeScript code snippet below: type Primitive = undefined | null | boolean | number | string; // A POJO is simply meant to represent a basic object, without any complexities, where the content is unknown. interface POJO { [key: string]: ...

Easy pagination for angular's in-memory-web-api

Looking for help to implement pagination in Angular-in-memory-web-api. Currently, I have the following setup: import { InMemoryDbService } from 'angular-in-memory-web-api'; export class InMemoryDataService implements InMemoryDbService { ...

Unlocking the Power of Angular 12: Leveraging the Subscribe Method to Access Multiple REST APIs

We have a task where we need to make multiple REST API calls from the ngOnInit() method, one after the other. After making the first call, we need to pass the response to the second API call, and similarly for the third call, we need to get the value from ...

Transmit data from a child component to a Vue JS page through props, and trigger the @blur/@focus function to access the prop from the parent component

Seeking guidance on working with props in Vue JS. As a newcomer to Vue, I hope that my question is clear. I've included my code snippet below, but it isn't functioning correctly due to missing Vue files. In my attempt to use a prop created in t ...

What could be the reason for receiving an undefined user ID when trying to pass it through my URL?

Currently, I am in the process of constructing a profile page and aiming to display authenticated user data on it. The API call functions correctly with the user's ID, and manually entering the ID into the URL on the front end also works. However, wh ...

Facing a problem with the carousel in Angular 6

I am currently working with Angular 6 and I have a topAdvertisementList[] that is supposed to return 2 records to be displayed in my carousel with a fixed image, but for some reason, only one record is showing up in the carousel! I suspect there might be a ...

Angular 7 automatically updates array values with the most recent values, replacing any previous values in the process

Currently, I'm working with arrays and utilizing the map function to modify the data. Below is an array of icons: private socialIcons:any[] = [ {"icon":"thumbs-up","operation":"like"}, {"icon":"thumbs-down","operation":"unlike"}, {" ...

The URL is reverted back to the previous address

Currently in the process of developing an Angular application, I've encountered a minor visual issue. On one of the pages, there is a ReactiveForm implemented, but whenever I navigate to that page, the URL reverts back to the previous one (even though ...