A project I’m currently working in required a retry component on the front-end, so, should a request fail for some reason, an error message with a “retry” button would appear where the request was supposed to be rendered if it was successful.

A “retry component” example

This sounds very simple at a first glance, just have a div with some text and a button that re-runs the request, and show them when the request fails. But this would appear at least once in most pages, and way more than once in some pages, so there had to be a component to do this which could be easily inserted on any part of the app and work with any request.

In order to implement this component I decided to take a step back and analyze how http requests work and how they are handled in Angular.

Simplifying things a little bit, the process of making a http request could be broken down into three possible states:

the request is still loading

the request finished with success

the request finished with an error

One way of implementing these in Angular is by using HttpClient to create an observable that makes a request to an endpoint when subscribed to, and then subscribing to that observable passing a success and a error callback to the subscribe function.

Making an http request the usual way

From here, it was necessary to devise a way of going back from the error state to the loading state.

The first idea was to just pass the executeService() function to the retry component, binding it to the context of the original component (by doing something like [retryFunction]=”executeService.bind(this)” ). This function could then be executed by the retry button to reset the variables used to control which state to render (loading and serviceError in the example above) and to subscribe again to the endpoint.

This works, but it’s necessary to create a new function on the caller component class for each retriable observable, and some components would have too many of these. Because of that I wasn’t too happy with this method.

To avoid the need to create a function for each retriable request, the second (and final) idea was to embed all logic on the observable itself and then input it to the retry component, which would simply have to subscribe to the observable to retry.

Or, to be more specific, the idea was to transform the observable in a way that, when it is subscribed to, it would execute some operations to reset values to their pre-subscription values, make the http request and upon completion call the appropriate callback.

Embedding the success and error callbacks into the observable itself is just a matter of piping them with the map (or tap) and catchError operators.

For resetting values, there isn’t a rxjs pipe operator to run some code right before subscribing to the observable, but the defer function can be used for the job.

The defer function takes an observable factory function (or in other words, a function that do some operations and then return an observable) as a parameter and return an observable that, when subscribed to, will run the factory function and subscribe to this new observable.

This can be exploited by inserting the variable resetting code on the factory function.

This work as intended, but the code doesn’t look so good. One way of making this better would be to make a custom “pipe-able” operator with the same behavior of using the defer function, which I’m calling doOnSubscribe().

A rxjs operator needs either to be or to return a function that takes an observable as a parameter and returns an observable (possibly the original observable but with some extra stuff).

To copy the behavior of using the defer function in a pipe you can create a doOnSubscribe function that takes as parameter a function that does what you want to be done on subscription. The doOnSubscribe then return another function which takes an observable as parameter, and executes what you want within the factory of a defer.

doOnSubscribe operator that can be “piped” in a rxjs Observable

This may sound complex at a first glance, but the result is that now there’s a doOnSubscribe operator, and now it’s possible to easily embed all the wanted behaviors (reset on subscribe, success callback, error callback) on the observable itself.

Now it’s possible to build a retry component taking as input only an observable like the defined above, and a retry button could work simply by subscribing to the observable.

You can check an example project I made with everything set up. The code can be found at my github page.