What is the best way to retry an action stream observable in Angular/RxJS after it fails?

Kindly disregard the variable names and formatting alterations I've made.

I've been attempting to incorporate RxJS error handling for an observable that triggers an action (user click) and then sends the request object from our form to execute an HTTP call, as demonstrated below.

Problem: When I send a request (from a form) on our website that doesn't exist in the backend, it fails and returns an error (which is good!), BUT if I try to submit a correct request after the failed attempt, the application just hangs and loads without any response.

serivce.ts file

sendRequest$ = this.exampleRatingSubjectAction$.pipe(switchMap(exampleRequest => this.http.post<exampleresponse>(this.PCFUrl + '/example',exampleRequest).pipe(
  catchError(this.handleError)
 )),
 tap(data => console.log(data)),
 );

sendExampleRequestEconomy$ = this.exampleSubjectActionEconomy$.pipe(switchMap(exampleRequest=>  this.http.post<exampleresponse>(this.PCFUrl + '/example',exampleRequest)
 .pipe(
  catchError(this.handleError)
 )),
 tap(data => console.log(data)),
 );

private handleError(err: HttpErrorResponse): Observable<never> {
// in a real world app, we may send the server to some remote logging infrastructure
//instead of just logging it to the console
let errorMessage: string;
if (err.error instanceof ErrorEvent) {
// A client-side or network error occured. Handle it accordingly
errorMessage = `An error occured: ${err.error.message}`;
} else {
// The backend returned an unsuccessful response code.
// the resposne body may contain clues as to what went wrong,
errorMessage = `Backend returned code ${err.status}: ${err.message}`;
}
console.error(err);
return throwError(errorMessage);
}

component code:

exampleResponsePriority$ = this.dataService.sendExampleRequest$
.pipe(
   map(data => this.exampleResponsePriority = data),
   tap(data => { 
    this.showPageLoader = false; 
    this.calculatorService.checkMinOrDeficitRate(data);
}),
catchError( err => {
   this.showPageLoader = false;
   // await this.presentAlert(err);
   this.errorMessageSubject.next(err);
   console.log("priority")
   return EMPTY;
}));

exampleResponseEconomy$ = this.dataService.sendExampleRequestEconomy$
.pipe(
  map(data => this.exampleResponseEconomy = data),
  catchError( err => {
    this.showPageLoader = false;
    // await this.presentAlert(err);
    this.errorMessageSubject.next(err);
    console.log("economy")
   return EMPTY;
}));

My only speculation is that our action stream has already been updated with a request object on the first click, so when the backend response is a 500 error, it might be stuck and not allowing for a resubmit?

I've attempted to modify the action stream upon a failed request, but couldn't achieve the desired result.

I believe I'm just a few lines of code away!

Any assistance would be greatly appreciated.

Answer №1

Issue

It seems that in the final catchError block within your component code, you are returning the EMPTY RxJS stream as the error response. The RxJS documentation states that:

This operator deals with errors and passes all other events to the resulting observable. If the source observable ends with an error, it will map that error to a new observable, subscribe to it, and pass on all its events to the resulting observable.

You can find more information on this in the official documentation, with emphasis provided.

Since your source observer ended with an error, it skips those previous steps in the pipeline. Consequently, the subscription moving forward is listening to EMPTY (documentation link), causing the subscription handler not to be invoked (and the subscription itself not to end).

Solution

A potential solution could be to return the source observable passed to catchError as the second argument within the handler. This adjustment may look like the following in your code:

exampleResponsePriority$ = this.dataService.sendExampleRequest$
.pipe(
   map(data => this.exampleResponsePriority = data),
   tap(data => { 
    this.showPageLoader = false; 
    this.calculatorService.checkMinOrDeficitRate(data);
}),
catchError((err, caught) => {
   this.showPageLoader = false;
   // await this.presentAlert(err);
   this.errorMessageSubject.next(err);
   console.log("priority")
   return caught;
}));

You might also want to explore the retry operator documentation, which can serve as an alternative or complement to catchError for achieving similar outcomes.

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

Add a new module to your project without having to rely on the initial

I'm looking to experiment with a module by making some adjustments for testing purposes. You can find the code here. Currently, all I need to do is type "npm install angular2-grid" in my terminal to add it to my project. Here's my concern: If ...

The error at core.js line 4442: TypeError - Unable to access the 'trim' property of an undefined value

I encountered an issue with the 'trim' function in my Angular Node MySQL CRUD Application core.js:4442 ERROR TypeError: Cannot read property 'trim' of undefined at AdmissionListComponent.post (admission-list.component.ts:30) at Admissi ...

The index.ngfactory.ts file threw an unexpected token error, indicating that an appropriate loader may be necessary to handle this specific file

I've spent several hours trying to troubleshoot this persistent error, exhausting all online resources for solutions. The issue arises consistently with every module of Angular Material only during the build process using --env.prod. The webpack confi ...

What is causing this CORS error in my Angular request?

I currently have a Controller implemented in Angular 4 that makes a request to the NBA API Stats. When I test this request in Postman, I can see all the data without any issues. However, when I execute the same request in my Angular application, I encounte ...

Angular ListView and GridView is a powerful feature that allows developers to display

I am looking to create Listview and Gridview components using Angular with the following features: Paging Sorting Filtering Searching In addition, the Gridview should also have support for Nested Gridview. What are currently the most commonly us ...

Exploring the World of Angular6 with the Incredible Power of tinyMCE Editor

Hello, I'm looking to incorporate the tinyMCE editor into my Angular 6 application. I followed a thread that explains how to integrate it, but I ran into an issue where it says the domain is not registered. I would prefer to do this without requiring ...

Troubleshooting ngModel Binding Issue with Sub-Children in CKEditor 5 and Angular 7

Firstly, I am sharing my code below: ListPagesComponent: export class ListPagesComponent { public model = { id: -1, actif: 0, link: '', htmlContent: { fr: '', ...

Having trouble retrieving an array element within an HTML table

Currently, I am working on a database project related to bar beer drinkers. To display the results of my queries on a local host website, I am running queries on my database and using Angular. The issue I am facing is that while I can see the query executi ...

Is it possible to both break down a function parameter and maintain a named reference to it at the same time?

When working with stateless functional components in React, it is common to destructure the props object right away. Like this: export function MyCompoment({ title, foo, bar }) { return <div> title: {title}, ...</div> } Now ...

Obtaining attribute data value upon selection change in Angular 4

Having trouble retrieving the value from data-somedata in my code... <select class="form-control input-sm" [(ngModel)]="o.id" formControlName="optionals" (change)="menuChange($event)"> <option *ngFor="let menu_optional of menu_optionals" value= ...

Angular 2 System.config map leading to a 404 error message

I am encountering a 404 error in the browser console while attempting to map the Auth0 module from node_modules in my Angular 2 project using the system.config in the index file. Index File <!-- 2. Configure SystemJS --> <script> System.con ...

Eliminate pipe operators from RXJS Observables

I am facing a challenge where I need to modify the operators applied to an observable by removing or adding new ones. Here's the scenario: Suppose I have the following immutable observable: let objects$ = of([{ category: 1, name: 'S ...

I am having trouble figuring out the issue with the state and how to properly implement it in Typescript

I am having difficulties sending emails using Nodemailer, TypeScript, and NextJS. The contact.tsx file within the state component is causing errors when using setform. As a beginner in TypeScript, I have been unable to find a solution to this issue. Any he ...

What could be the reason for the defaultCommandTimeout not functioning as expected in my script

Is there a way to wait for only one particular element in Cypress without having to add wait commands everywhere in the test framework? I've come across the solution of adding defaultCommandTimeout in the cypress.json file, but I don't want it t ...

Ensuring the image is properly sized to fit the screen and enabling the default zoom functionality

I am looking to achieve a specific behavior with an image, where it fits the viewport while maintaining its aspect ratio and allowing for zooming similar to how Chrome or Firefox handle local images. Here are the details of my project: The image I have is ...

Using TypeScript to automatically determine the argument type of a function by analyzing the return type of a different function

I am working on an interface with the following structure: interface Res<R = any> { first?(): Promise<R>; second(arg: { response: R }): void; } However, I noticed that when creating a plain object based on this interface, the response ...

unable to locate the nested routes in the folder for remix

Hey there, I've been using the remix to create a basic page and I'm trying to organize the routes using folders. Here is my project directory: - app/ - root.tsx - examples/ - route.tsx - child1/ - account.tsx In the examples di ...

Obtaining RouteParams in the loader function of RouteConfig can be achieved by following a

Is there a way to achieve the following setup: @RouteConfig([ { path: '/example/:name', name: 'Example', loader: (params: RouteParams) => { let name = params.get('name'); return _EXAM ...

Stop Angular 2 from loading on Internet Explorer

I'm looking for a way to stop my Angular 2 application from loading on any version of Internet Explorer. I attempted using a script tag in index.html to detect IE, which was successful. However, when trying to redirect the app to a "not compatible pag ...

Exploring observables for querying the OMDB API and obtaining information on movies

Hey everyone, I'm currently working on implementing a live search feature using Observables in Angular2 to fetch Movie data from the OMDB API. While I can see that it is functioning correctly in the Chrome Network tab, the results aren't showing ...