Ways to initiate a fresh API request while utilizing httpClient and shareReplay

I have implemented a configuration to share the replay of my httpClient request among multiple components. Here is the setup:

apicaller.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
// import rxjs map
import { map, shareReplay } from 'rxjs/operators';

@Injectable({
  providedIn: 'root',
})
export class ApicallerService {
  peopleindex: number = 1;
  constructor(private _http: HttpClient) {}

  private readonly request = this._http
    .get('https://swapi.dev/api/people/' + this.peopleindex)
    .pipe(shareReplay());

  getData(peopleindex: number = 1) {
    this.peopleindex = peopleindex;
    return this.request;
  }
}

Component1

This component should request the API to retrieve details for id 2

import { Component, OnInit } from '@angular/core';
import { ApicallerService } from '../apicaller.service';

@Component({
  selector: 'app-component1',
  templateUrl: './component1.component.html',
  styleUrls: ['./component1.component.css'],
})
export class Component1Component implements OnInit {
  apiresponse: any;
  editRes: any;
  constructor(private _apicaller: ApicallerService) {}

  ngOnInit(): void {
    this._apicaller.getData(2).subscribe((data: any[]) => { // <-- Notice 2 in the argument
      console.log(data);
      this.apiresponse = data;
    });
  }
}

Component2

This component requests data for the default ID

import { Component, OnInit } from '@angular/core';
import { ApicallerService } from '../apicaller.service';

@Component({
  selector: 'app-component2',
  templateUrl: './component2.component.html',
  styleUrls: ['./component2.component.css']
})
export class Component2Component implements OnInit {

  apiresponse: any = {};
  constructor(
    private _apicaller: ApicallerService
  ) { }

  ngOnInit(): void {
    // get data from api using service
    this._apicaller.getData().subscribe(
      (data) => {
        this.apiresponse = data;
      }
    );
  }

}

However, there seems to be an issue where component1 is not making a new request with peopleindex = 2.

To see and reproduce the problem, you can check out my StackBlitz setup here: https://stackblitz.com/edit/angular-ivy-gdxyy6?file=src%2Fapp%2Fcomponent1%2Fcomponent1.component.ts

Answer №1

One recommendation is to establish an API store for scenarios like this.

  • Initially, the API will be stored with a specific ID and return the initial call.

  • Subsequently, the stored API will be utilized on subsequent calls.

export class ApicallerService {
  apiStore = new Map();
  constructor(private _http: HttpClient) {}

  private getApiRequest(id) {
    return this._http.get('https://swapi.dev/api/people/' + id).pipe(shareReplay());
  }

  getData(peopleindex: number = 1) {
    if (!this.apiStore.has(peopleindex)) {
      this.apiStore.set(peopleindex, this.getApiRequest(peopleindex));
    }
    return this.apiStore.get(peopleindex);
  }
}

Answer №2

Utilizing ShareReplay can be advantageous when you require consistent metadata (masterdata) that remains the same across various components, allowing for cached or shared data.

To ensure the expression is executed with updated values, it's essential to include a getter for the request instead of directly accessing the field.

private get request() { 
    return this._http.get('https://swapi.dev/api/people/' + this.peopleindex);
}

The common practice for making httpService calls involves creating a dedicated service to handle REST endpoints by wrapping around the httpClient.

@Injectable({
  providedIn: 'root',
})
export class ApiService {
  constructor(private _http: HttpClient) {}

  get(url: string) {
    return this._http
    .get(url);
  }

  post(url: string, data) {
    return this._http
    .post(url, data);
  }

}

Subsequently, establish a feature service that relies on the apiService.

@Injectable({
  providedIn: 'root',
})
export class PeopleService {
  constructor(private _api: ApiService) {}

  getData(peopleindex: number = 1) {
    console.log(peopleindex);
    return this._api
    .get('https://swapi.dev/api/people/' + peopleindex);
  }
}

Invoke these feature services within your respective feature components.

export class Component1Component implements OnInit {
  apiresponse: any;
  editRes: any;

  constructor(private _peopleService: PeopleService) {}

  ngOnInit(): void {
    this._peopleService.getData(2).subscribe((data: any[]) => {
      this.apiresponse = data;
    });
  }
}

export class Component2Component implements OnInit {
      apiresponse: any = {};
      constructor(
        private _peopleService: PeopleService
      ) { }

      ngOnInit(): void {
        // Obtain data from API using the service
        this._peopleService.getData().subscribe(
          (data) => {
            console.log(data);
            this.apiresponse = data;
          }
        );        
      }
    }

For further reference, you can access the StackBlitz link 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

What strategies can be utilized to manage a sizable data set?

I'm currently tasked with downloading a large dataset from my company's database and analyzing it in Excel. To streamline this process, I am looking to automate it using ExcelOnline. I found a helpful guide at this link provided by Microsoft Powe ...

Troubleshooting property assignment issues within an Angular service

I have created a simple injectable service in TypeScript for Angular 4. This service is designed to fetch a JSON file from the network and store the data as an array in its property called data. Other classes can then access this data using the method getD ...

Deactivate the button in the final <td> of a table generated using a loop

I have three different components [Button, AppTable, Contact]. The button component is called with a v-for loop to iterate through other items. I am trying to disable the button within the last item when there is only one generated. Below is the code for ...

Obtain the object literal string with additional decorative strings surrounding it

In my current Typescript code, I have an object literal structured like this: const MyNamesStrings = { a: { b: "hello", c: "bye" } d: { e: "qwerty" } } However, I am looking for a way to wrap these strings with add ...

Is there a disparity in capabilities or drawbacks between ViewChild and Input/Output in Angular?

As I delve into Angular ViewChild and compare it to Input/Output parameters, I can't help but wonder if ViewChild has any drawbacks or limitations compared to Input/Output. It appears that ViewChild is the preferred method, as all parameters are now ...

communicating between client and server by sending http requests within the Heroku platform

My application utilizes Angular on the front end and Node.js on the backend. I am encountering some difficulties when it comes to configuring them properly for production deployment on Heroku. During development, my Node.js server runs on port 3000 and al ...

When trying to pass 3 parameters from an Angular frontend to a C# MVC backend, I noticed that the server side was receiving null

I have encountered an issue where I am attempting to pass 3 parameters (2 types and one string) but they are showing up as null on the server side. Below is my service: const httpOptions = { headers: new HttpHeaders({ 'Content-Type&ap ...

Tips for incorporating the observer design pattern in REST APIs (Communication between front-end and back-end)

Is it possible to subscribe once to an API and receive multiple responses until I unsubscribe from that event? If so, how can this be achieved? If not, why does this approach not align with the observer pattern's guidelines? I attempted using the yie ...

Applying background-image in ngStyle results in an undefined value

I have been attempting to incorporate images (retrieved through an API) as the background-image of an element. However, I keep encountering the error message Cannot read property 'url' of undefined, even though the URL is actually being rendered. ...

Issue with Typescript not recognizing default properties on components

Can someone help me troubleshoot the issue I'm encountering in this code snippet: export type PackageLanguage = "de" | "en"; export interface ICookieConsentProps { language?: PackageLanguage ; } function CookieConsent({ langua ...

Exploring the functionality of custom hooks and context in asynchronous methods using React Testing Library

I'm currently testing a hook that utilizes a context underneath for functionality This is how the hook is structured: const useConfirmation = () => { const { openDialog } = useContext(ConfirmationContext); const getConfirmation = ({ ...option ...

Error encountered: No matching overload found for MUI styled TypeScript

I am encountering an issue: No overload matches this call. Looking for a solution to fix this problem. I am attempting to design a customized button. While I have successfully created the button, I am facing the aforementioned error. Below is my code ...

Sending data from child components to parent components in Angular

I'm facing an issue with retrieving data from a child array named store within user data returned by an API. When trying to access this child array in my view, it keeps returning undefined. Code export class TokoPage implements OnInit { store= nu ...

Securing Your Angular 2 / ASP.NET MVC Application from CSRF Attacks

I am currently developing a single-page application using ASP.NET MVC for the backend and Angular 2 for the frontend. To prevent cross-site request forgery attacks in my application, I have implemented the following steps: Since ASP.NET MVC sends a co ...

Importing SCSS files dynamically with TypeScript

Recently, I utilized Create React App (CRA) to create a new project and then included node-sass in order to import SCSS files. An example: import "./App.scss"; Although this method works without any issues, I encountered a problem when trying t ...

What are the best practices for utilizing fetch() to retrieve data from a web API effectively?

Is there a way to store stock data in stockData and display it in the console upon form submission? Upon submitting the form, I only receive undefined. I suspect this may be due to a delay in fetching data from the API (but not certain). How can I resol ...

Navigating Routes with Router in Angular 7: A Step-by-Step Guide

Within my sidebar navigation component, the sidebar.component.html file is structured as follows: <nav class="navbar navbar-expand-lg navbar-dark bg-primary fixed-top" id="sideNav"> <a class="navbar-brand" href="#page-top"> <span cl ...

Guide on inserting Angular 2 attributes within a variable in an Angular 2 TypeScript class

I have a variable 'content' in the 'findCharityHome.ts' typescript class structured like this. let content = ` <b>`+header+`</b> <button style='background-color:#428bca;color:white; ...

reasons why my custom attribute directive isn't functioning properly with data binding

Here is a snippet of the code I am working on, with some parts omitted for brevity: template.html ... <tr *ngFor="let item of getProducts(); let i = index" [pa-attr]="getProducts().length < 6 ? 'bg-success' : 'bg-warning'" ...

Create an array form with the first input at index 0 filled out

Currently, I am in the process of developing a form that allows users to update, add, delete, and edit entries using Angular 2 and reactive forms. To achieve this, I have utilized a form builder array to dynamically add input controls to the form array. Ev ...