What is the most efficient way to trigger an API call using Angular HttpClient in response to multiple events without repetitively subscribing to the API call method throughout my code?

In my Angular Cli app, I have a data table with dynamic filters and a search bar. Whenever the search value changes, pagination changes, or a filter is applied or removed, I need to update the table items and filter information by making an API call.

My service has a function that handles the API call and returns the response:

getFilters() {
    const body = { searchTerm: 'foo' }; // 
    return http.post(url, body)
        .map(res => res)
        .catch(err => Observable.of(err);
}

getTableItems() {
    const body = { searchTerm: 'foo', ...stuff for filtering }; // 
    return http.post(url, body)
        .map(res => res)
        .catch(err => Observable.of(err);
}

getApiData() {
    const filters = this.getFilters();
    const items = this.getTableItems();
    return forkJoin([ filters, items ]);
}

Since I have multiple methods triggering API calls, I'm duplicating the

getApiData().subscribe(res => doSomethingWithResponse(res));
in the constructor and each trigger method.

How can I avoid this repetition and maintain a DRY codebase? I utilize @ngrx/store for data storage on the Front End and work with rxjs Observable's and BehaviorSubject's.

Answer №1

Your application relies on data from the API as one of its primary sources. Various events can trigger changes in the data:

  1. search queries
  2. pagination
  3. filters

To display posts in your component, you will need to use the posts$ observable as the source of posts.

<div *ngFor="let post in posts$ | async">{{post}}</div>

You need to define this observable in your component:

posts$: Observable<Post>;
ngOnInit() {
    const body = {};
    this.posts$ = http.post(url, body);
}

But currently, no parameters are being used. Let's identify the sources of changes and define them as observables:

posts$: Observable<Post>;
page$: Observable<number>;
searchTerm$: Observable<string>;
otherFilters$: Observable<Filters>;

In the ngOnInit() hook, we will focus on pagination and search input as the sources of events:

ngOnInit() {
    this.page$ = this.route.paramMap
        .switchMap((params: ParamMap) => params.get('page'));
    this.searchTerm$ = this.searchTerm.valueChanges
        .startWith('');
    
    const body = {};
    this.posts$ = http.post(url, body);
}

To trigger new data downloads whenever there's a change in page$ or searchTerm$, define the posts$ observable based on these two:

this.posts$ = Observable.combineLatest(this.page$, this.searchTerm$, (page, searchTerm) => ({ page, searchTerm}))
    .switchMap(params => {
        let body = params;
        return http.post(url, body);
    });

Pay attention to the transform function in combineLatest, which creates a single object with values from each observable for the switchMap to use. By composing observables in this way, you can subscribe to the final data source. Remember to use the async pipe for subscription to manage automatic unsubscribing. I hope this explanation is helpful! 👍

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

How can I combine Angular's material drag and drop feature with virtual scrolling?

I've been attempting to combine Angular material's virtual scrolling with drag and drop functionality, but I'm encountering an issue where the items are reverting back and the dragging and dropping doesn't seem to work as expected. Bel ...

Creating dynamic checkboxes and dropdown menus in Angular 8 by fetching data from an API

Take a look at my Stack Blitz project: here I am trying to achieve two things: hide the dropdown if the divisions are empty, and set the default value of the dropdown to the first one in the list. Can anyone guide me on how to accomplish this? Your help ...

"Setting Up a Service in Angular: A Step-by-Step Guide

I am facing an issue with a root service that contains composition within it, as shown below: import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root', }) export class MapService { private rmap: RMap; ini ...

Is it possible to use an object's attribute as a switch case in TypeScript with useReducer?

I am attempting to convert switch case String into an object, but for some reason typescript is misunderstanding the switch case within useReducer: Prior to version update, everything was functioning correctly: export const LOGIN_USER = "LOGIN_USER&qu ...

Tips for converting mat-paginator in Angular 4?

Can you provide any suggestions on how to translate the phrase "Items per page" within the Angular mat-paginator tag, which is a component of Material Design? ...

What is the process of determining if two tuples are equal in Typescript?

When comparing two tuples with equal values, it may be surprising to find that the result is false. Here's an example: ➜ algo-ts git:(master) ✗ ts-node > const expected: [number, number] = [4, 4]; undefined > const actual: [number, number] ...

Developing a feature in Angular 4 where shared components generate new instances with each reload

My dashboard template is based on Core UI, but I have made some modifications to better suit my app. The template consists of modules, with the sidebar menu playing a crucial role in navigation. However, as my app is larger, I utilize the top menu for navi ...

Having trouble with a nested Angular 2 component not showing up on the page?

I'm currently developing a mobile app with Angular 2 and Nativescript. In my setup, I have the HomeComponent, which includes a DockComponent. The DockComponent is functioning correctly, but now I want to break down the four buttons in the dock into se ...

Tips for testing Observable.fromEvent

Can you provide a method to test Observable.fromEvent using jasmine? @ViewChild('d') private inputDatePicker: NgbInputDatepicker; this.subscription = Observable.fromEvent(document, 'click').subscribe((event: KeyboardEvent) => { ...

Angular: Leveraging real-time data updates to populate an Angular Material Table by subscribing to a dynamic data variable in a service

Seeking guidance on how to set up a subscription to a dynamic variable (searchData - representing search results) for use as a data source in an Angular Material Table. I have a table-datasource.ts file where I want to subscribe to the search results from ...

Angular2 poses a strange problem with ngClass

It seems like Angular is expecting me to use single quotes for the class names even if there are no hyphens in my CSS class name. I've tried everything but Angular keeps insisting on using hyphens for this specific CSS class... it's strange, or m ...

Issue with Angular/Jasmine: Undefined property 'pipe' not readable

I have been struggling to resolve a problem with Angular 9, Jasmine, and RxJS without much success. While my unit tests run successfully in Jasmine, there are certain lines of code that do not get executed. Despite scouring multiple posts for assistance, ...

Typescript enhances React Native's Pressable component with a pressed property

I'm currently diving into the world of typescript with React, and I've encountered an issue where I can't utilize the pressed prop from Pressable in a React Native app while using typescript. To work around this, I am leveraging styled comp ...

The spread operator seems to be malfunctioning whenever I incorporate tailwindcss into my code

Hi there! I hope you're doing well! I've come across a strange issue in Tailwindcss. When I close the scope of a component and try to use props like ...rest, the className doesn't function as expected. Here's an example: import { Butto ...

What's preventing me from using just one comparison condition in TypeScript?

The issue at hand is quite simple: An error occurred because I tried to compare a number with a 'Ref<number>' object. It seems ridiculous that I can't compare two numbers, but as I am new to Typescript, I would greatly appreciate some ...

Testing Angular Library Before Release

My Angular library consists of modules, each containing services. I am looking for a way to easily test my library when I make changes to one of the services. Is there a simple solution that doesn't involve publishing and installing it in the main pr ...

What is the best way to exhibit information from a get request within an option tag?

My GET request is fetching data from a REST API, and this is the response I receive: { "listCustomFields": [ { "configurationType": null, "errorDetails": null, "fieldId" ...

What is the best way to arrange and manage assets?

I created a component with an image in it. The current file structure is like this: https://i.sstatic.net/hkZWG.png Now, my query is whether placing the assets folder inside the web folder is the right choice or not? UPDATE This is what I tried: impor ...

Struggle with implementing enums correctly in ngSwitch

Within my application, I have implemented three buttons that each display a different list. To control which list is presented using Angular's ngSwitch, I decided to incorporate enums. However, I encountered an error in the process. The TypeScript co ...

Tips for resolving the issue where a radio group in Angular does not impact other items

Here's the code list.component.html <form nz-form [formGroup]="taskFormGroup" (submit)="saveFormData()"> <div nz-row *ngFor="let remark of checklis> <div nz-col nzXXl="12" *ngFor="let task of remark.tasks" style="pad ...