What is the operator to conditionally chain Observables together?

My goal is to extract paginated data from a REST API and integrate it into my Angular application. The structure of the data sent by the API typically looks like this:

{
    "next": null,
    "results": [
        {"id": 7, "name": "Alicia"},
        {"id": 8, "name": "Ted"},
        {"id": 9, "name": "Marshall"}
    ]
}

The next field contains the URL for retrieving the next page of data. Since I don't know in advance how many pages are needed to load all the data, I need a flexible solution.

I have implemented a functional method that retrieves the data and can be viewed in this working example:

public loadPeople( next?:string ): void {
    if(!next) next = 'api/1.json';

    this.http.get(next)
        .pipe(
          map( (response: Response) => response.json())
        )
        .subscribe( (data: any) => {
          this._people = this._people.concat(data.results);
          this._peopleSubject.next(this._people);
          if(data.next) this.loadPeople(data.next);
        })
}

Although my current code works, I believe there might be a more efficient way to achieve this using RxJS operators to chain Observables. If anyone has suggestions on which operator to use or how to improve the code, I would appreciate the help. Thank you!

Answer №1

To implement this functionality, leverage the power of RxJS operators like concatMap and concat:

public fetchData( endpoint:string ): Observable<string[]> {
    return this.http.get(endpoint)
        .pipe(
          map( (response: Response) => response.json() )
          concatMap((data: any) => {
            if (data.next) {
              return Observable.of(data.results).concat(this.fetchData(data.next));
            }
            return Observable.of(data.results);
          })
        );
}

public fetchItems( endpoint?:string ): void {
    if(!endpoint) endpoint = 'api/1.json';

    this.fetchData(endpoint)
        .subscribe( (items: string[]) => {
            this._items = this._items.concat(items);
            this._itemsSubject.next(this._items);
        })
}

Make sure to include these necessary imports:

import { of } from 'rxjs'
import { concat, concatMap,  map } from 'rxjs/operators';

Answer №2

To achieve the same result, you have the option of using Map and Subscribe or MergeMap. Map and subscribe can be implemented as:

this.http.get('/api/people/1')
  .map(res => res.json())
  .subscribe(character => {
    this.http.get(character.homeworld).subscribe(homeworld => {
      character.homeworld = homeworld;
      this.loadedCharacter = character;
    });
  });

}

However, there are two observations to be made from this approach:

  • Firstly, we are creating a nested pyramid structure with our Observables which can make the code less readable.

  • Secondly, our two requests were executed sequentially.

Therefore, an alternative method is to use mergemap to iterate over the Observable values, like so:

this.homeworld = this.http.get('/api/people/1')
  .map(res => res.json())
  .mergeMap(character => this.http.get(character.homeworld))

}

Credit goes to ** Cory Rylan** for providing a clear explanation, found at enter link description here

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

Ways to display Div contents based on selected options in an Angular 2/4 list

"Struggling with dynamically displaying div contents" Situation:- I have a list of 3 items: *Menu1 *Menu2 *Menu3 and I have separate div contents for each of these menu items. Requirement :- By default, Menu1 should be displayed. When Menu2 is clicked, ...

The rendering of the list is taking an unexpectedly long time due to random factors

I have encountered a strange issue with one of my components. The component fetches a list of objects from a service in the ngOInit(). The problem I am facing seems to occur randomly, where sometimes it takes a considerable amount of time to display this l ...

Is it achievable to validate image dimensions using angular forms? Or, how can one go about validating image dimensions through angular forms?

I have encountered an issue with using Angular forms, file input type, and custom validators. Whenever the form value changes, I only receive a fake path URL. This fake path URL does not contain any file metadata, so how can I validate image dimensions? ...

At runtime, the array inexplicably becomes null

Having recently ventured into the world of Ionic framework development, I have encountered a puzzling issue. At runtime, an array inexplicably gets nulled and I am struggling to pinpoint the root cause. export interface Days { name:string; } @Compon ...

Once the data being interpolated undergoes a change, implement a setTimeout function in Angular 7

To hide the element with a specific value when displayed I attempted to monitor the incoming message (which changes from an old value to a new one) and added a timeout to the new message. However, there is a delay between the message arriving and appearin ...

Angular with Entypo Icons

If you want to use the Entypo SVG icon set in your Angular application, you will need to add some JavaScript to the page. Here is an example of how you can do this: const entypo = require('entypo') document.body.insertBefore(entypo.getNode(), d ...

Tips on streamlining two similar TypeScript interfaces with distinct key names

Presented here are two different formats for the same interface: a JSON format with keys separated by low dash, and a JavaScript camelCase format: JSON format: interface MyJsonInterface { key_one: string; key_two: number; } interface MyInterface { ...

Creation of Card Component with React Material-UI

I am facing difficulties in setting up the design for the card below. The media content is not loading and I cannot see any image on the card. Unfortunately, I am unable to share the original image due to company policies, so I have used a dummy image for ...

Explore an example of using custom controls in a FormArray within an Angular 5 reactive form on StackBlitz

Check out my sample project on stackblitz that tests nesting components and retrieving the complete form value. https://stackblitz.com/edit/mensand-hobbies-football-tennis This is a demonstration where I aim to utilize different components stored in an a ...

Ways to eliminate toggle event in Angular

I've been doing a lot of research online, but all the solutions I find are using jquery. I'm still getting the hang of Angular and Typescript. I found this and this to be unhelpful. I built a monthpicker from scratch, which has a simple and clear ...

Controlling numerous websockets using React

I am currently developing a single-page application using React.js with a JSON RPC 2.0 backend API that relies on websockets. Managing multiple websocket connections simultaneously across different React.js components has been a challenge. Initially, I th ...

How can I export a function signature in TypeScript?

Is there a specific syntax to export a function from another module in an interface or a namespace? I couldn't find the right way to do it. What would be the correct syntax? import {myFunction} from './a' export interface MyInterface { ...

What are some ways to expand the width of a MaterialUI form control if no value has been chosen?

I am currently working on a project where I need a dropdown menu component with specific selections. However, the layout appears to be cramped and I'm struggling to adjust the width. Additionally, I've been unsuccessful in changing the font size ...

Deliver Compressed Files following Angular CLI --Prod Configuration

After using the Angular CLI's command to minify my basic Angular app, a dist folder was generated with the project folder and minified files. However, when I run ng serve, it always serves the unminified development files, whether it's in the roo ...

Ways to retrieve the initial 4 elements from an array or class organized by their price entries in ascending order

Let's say we have an array of objects representing products: Products: Product[] = [ { id: 1, name: 'Milk', price: '1' }, { id: 2, name: 'Flour', price: '20' }, { id: 3, name: 'Jeans', pri ...

Attempting to fill a template using ngfor, wherein the initial 2 elements are displayed in a row

I am attempting to complete a task where, using an ngFor directive to iterate through an array, I need to display the first 2 elements in a row and the remaining elements in a descending column. It should look like this: first second third fourth fifth ...

Encountered a Webpack issue when trying to load the primeng.min

I recently initiated a fresh project using yo aspnetcore-spa. My goal is to integrate the PrimeNG component library. Upon installing font-awesome and primeng: npm install font-awesome primeng --save I included CSS in the webpack vendor list: vendor: [ ...

Can you explain the significance of the colon in this context?

Upon reviewing some SearchKit code snippets (composed with react/jsx and es2015), I came across the following line in a jsx file: const source:any = _.extend({}, result._source, result.highlight) I am curious about the purpose or significance of the colo ...

Create collaborative documents with serverless TypeScript extension

Utilizing Amazon Lambda AWS along with Serverless and the Serverless Plugin TypeScript to develop my TypeScript files has been quite a challenge. I have implemented shared code in my project, organized within folders such as: /shared: shared1.ts, shared2. ...

Tips for resolving the "Page Not Found" error in your NextJs application

I am organizing my files in the following structure src/ ├── app/ │ ├── pages/ │ │ ├── authentication/ │ │ │ ├── SignUp/ │ │ │ │ └── page.tsx │ │ │ └── SignIn/ │ ...