"Implementing a retry feature for Angular http requests triggered by a button

Imagine having a situation where a component has a method that triggers an http request defined in a service. The observable is subscribed to within the component:

Component:

fetchData() {
    this.apiService.fetchDataFromServer().subscribe(
        response => console.log(response),
        error => console.error(error)
    );
}

API Service:

fetchDataFromServer() {
    return this.http.get();
}

In case of a request failure, it is desired to display an error modal with two buttons: "reload" and "cancel". Clicking on either button should close the modal.

One way to handle this issue inexpensively would be to simply re-trigger fetchData() from the error scenario in the component. However, handling errors at the service level would result in a cleaner solution.


I am currently investigating how to manage the button click event. By using pipe and catchError, it seemed impractical to patiently wait for the reload button click within the service method. Utilizing retryWhen seems like a potential solution, but I am struggling with the implementation.

Below is my attempt to enhance the API Service:

// The modal with the two buttons will trigger 
// - this.apiService.button$.next(true) if the user chooses to reload, or 
// - this.apiService.button$.next(false) if the user opts not to reload
button$ = new Subject<boolean>();

fetchDataFromServer() {
    return this.http.get().pipe(
        retryWhen((errors: Observable<any>) => {

            this.openErrorModal(); // a method to display the error modal

            // How can I incorporate the button$ subject here?
            // I attempted 
            // - return this.button$.pipe(), and 
            // - return errors.pipe(), 
            // but struggled to finalize the implementation

        }
    );
}

openErrorModal() {
    // Display a modal with a simple interface
}

The ultimate aim is for the component to receive the data or error regardless of the number of times the request was made. Essentially, the error modal should appear whenever the request fails, allowing the user to potentially enter into an infinite loop by choosing to reload continuously. Once the user clicks "cancel," further requests should cease.

Note: This problem has been streamlined significantly to focus solely on the core issue.

Answer №1

To achieve the desired behavior, consider modifying your design slightly. Instead of directly returning the observable from the getDataFromServer function, introduce a Subject in the middle.

This approach allows for implementing retry logic on the service in case of failures. By incorporating this as a foundational function in your services, you can easily encapsulate each call and prevent code redundancy.

The revised implementation would resemble:

tempSubject: Subject<any>;
getDataFromServer() {
  this.tempSubject = new Subject();    
  this.getDataFromServerInternal(this.tempSubject);
  return this.tempSubject;
}

private getDataFromServerInternal(sub: Subject < any > ) {
  this.httpClient
    .get()
    .subscribe(
      (res) => sub.next(res),
      (error) => {
        const confirmed = confirm(`try again? error: ${error}`);
        if (confirmed) {
          this.getDataFromServerInternal(sub);
        }
      }
    );
}

Ensure to properly complete and unsubscribe from all resources upon completion to prevent memory leaks. The method invocation from the component remains unchanged.

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

Tips for creating type-safe assertion functions in TypeScript

In TypeScript 3.7, a new feature allows the writing of "assertion functions." One example is shown below: export type TOfferAttrName = keyof typeof offerAttrMap; export const assertIsOfferAttrName = (name: string): asserts name is TOfferAttrName => { ...

Pagination in Laravel using Angular 5

Currently experiencing pagination issues in an Angular5 and Laravel5 project. The JSON values are as follows: { "products": { "current_page": 1, "data": [ ... ], "first_page_url": "http://localhost:8000/api/ ...

Retrieving the selector name from a JSON in Angular 2

I'm looking to create a dynamic form where element details will be sourced from JSON data. This is the JSON structure I have: { "FormElements": [ { "selectorName": "text-input", "id": "", "class": "location", "nam ...

Error in Mongoose Schema Configuration Detected in NestJS App

I'm currently developing an e-commerce application using NestJS and MongoDB with Mongoose. I've been facing an issue while trying to implement a user's shopping cart in the application. The error message I keep encountering is as follows: ...

How can I trigger a function after all nested subscriptions are completed in typescript/rxjs?

So I need to create a new user and then create two different entities upon success. The process looks like this. this.userRepository.saveAsNew(user).subscribe((user: User) => { user.addEntity1(Entity1).subscribe((entity1: EntityClass) => {}, ...

What is the best way to depict object key replacements within a Typescript definition?

I currently have these types: type PossibleKeys = number | string | symbol; type ValueOf<T extends object> = T[keyof T]; type ReplaceKeys<T extends Record<PossibleKeys, any>, U extends Partial<Record<keyof T, PossibleKeys>> = ...

Unable to post links on Facebook when using Angular 7

Is it possible to share a URL on Facebook using Angular 7 without server-side rendering like Angular Universal or prerender? I attempted to update meta tags for Facebook share during the click function, but it did not work. What is the most effective way t ...

Is there a way to turn off the warning overlay in a React application?

I’m currently using react-app-rewired and I am trying to figure out how to turn off the overlay that displays Typescript warnings whenever I compile. It seems like some warnings that are not caught by the VSCode Typescript checker pop up on this overlay ...

The Typescript Decorator is triggered two times

I submitted a bug report regarding Typescript because I suspect there is an issue, although I'm seeking additional insights here as well. This is the scenario. When running the following code: class Person { @IsValueIn(['PETER', ' ...

Is there a way to establish a pre-defined key in a mat-table?

I am fetching data from an API and I need to display it in a key-value format on a mat table. The keys are predefined and not provided by the API. The data retrieved from the API is shown here: image1 I want to display it on a mat table like this: mat ta ...

Retrieving the <html> tag within an Angular component

In an Angular component, the <body> and <head> tags can be accessed by injecting DOCUMENT as shown below: import { DOCUMENT } from '@angular/common'; import { Inject } from '@angular/core'; export class TestComponent { c ...

What is the best way to include rxjs in an npm library - as a dependency, peer dependency, or both?

After researching numerous posts and articles on dependencies versus peerDependencies, I am still not entirely certain what to do in my particular situation.... I have a library (which is published to a private npm repository) that utilizes rxjs; for exam ...

Angular - A Guide to Managing User Roles by Toggling Checkbox Values

In my current project, I am developing a web portal using Angular-7 for the frontend and Laravel-5.8 for the backend. Additionally, I am utilizing Laravel Spatie for User Role Management. Within the user.component.ts file: export class UsersComponent imp ...

Incorporate all photographs from a designated directory in the gallery into an Angular 6 PWA Application

Currently, I am developing a Progressive Web Application that requires me to showcase all images stored under a specific directory (for instance, all pictures saved in the "Downloads" folder on a mobile device) within a personalized grid view. I would lik ...

Steps for implementing a conditional rendering in your codeHere is a

I've encountered an issue while attempting to implement conditional rendering. The error I'm getting is Element implicitly has an 'any' type because expression of type 'number' can't be used to index type 'types&apos ...

When using TypeORM's findOneBy method, if the search result

In order for the entity to have both identifiers, I require it to possess the Id and the _id export class ScriptSequencesExecutionEntity { @PrimaryGeneratedColumn({ name: 'id' }) _id!: string; @ObjectIdColumn() id: number; @AutoMap() ...

I have a data.json file with a URL that I need to access in my view using Angular. How can I achieve this without relying on innerHTML?

Here is the JSON file data.json that I am referring to: { "id": 1, "title": "POC", "desc": "<a href='www.google.com'>HOMEPAGE</a>", "status": "done", "percentage_finished": 100 } I am tryi ...

Choose the default value of the select tag if hardcoded options are used with ngModel

When the value is set by using this.model.type = OPTION_NR1, Angular automatically sets the HTML attribute ng-reflect-model to 0. However, the desired value for this HTML attribute should be OPTION_NR1 in order to populate the drop-down with Option1 instea ...

Customize validation timing in Angular Reactive Forms

One common query about the conditional validator is understanding when exactly it gets triggered. Imagine a simple form with just two fields this.form = formBuilder.group({ emailRequired: [false], emailRecipients: [''] }); Now, let's s ...

Keeping track of the authentication state in AngularFire2 on page reload to verify if the user is logged

I am facing a challenge in my angular4 app that uses angularfire2. I need to determine if a user is logged in when the page loads. Logging in and out works fine, and I have set up a guard on the router to handle unauthorized access. In one example I came ...