0

Assume that we have a component with a method that performs a http request defined in a service. We subscribe to the observable somewhere in the component (could also be in the template):

Component:

getData() {
    this.apiService.getDataFromServer().subscribe(
        a => console.log(a),
        b => console.error(b)
    );
}

API Service:

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

When a request fails, I would like to open a modal that shows an error to the user. On this modal, the user finds two buttons: One says "reload", the other says "cancel". If one of the button is clicked, the modal closes again.

Now one (cheap) way to deal with this, would be to simply call getData() again from the error case in the component. Of course, it would be much nicer to handle errors on the service level. This is what I am trying to achieve here.


I am now trying to figure out how to handle the button click. With pipe and catchError, it was not possible to "wait" for the reload button click in the service method. I assume that a good way to deal with this would be retryWhen, but I cannot figure out how to do it.

Here is my attempt to extend the API Service:

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

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

            this.openErrorModal(); // this is a simple method that shows the modal to the user

            // how to use the button$ subject here?
            // I tried with 
            // - return this.button$.pipe(), and 
            // - return errors.pipe(), 
            // but I was not able to finalize it to make it work

        }
    );
}

openErrorModal() {
    // Open a modal with a simple GUI
}

In the end, my goal is that the component gets the data or error without knowing how often the request has been done. Basically, the error modal shall be opened every time the request fails, the user could thus keep himself in an infinite loop if he wants to. As soon as he clicks "cancel", no more requests shall be done.

Note: My problem is heavily simplified. I tried to remove all code that is not related to the problem.

0

1 Answer 1

2

You can change your design a bit to get the behavior you want. Add a Subject in the middle (instead of returning the observable from the getDataFromServer function).

This way, you can retry on the service whenever there is a failure, if you implement it as a base function your services, you can wrap each call with it and avoid code duplications.

It would look something like:

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);
        }
      }
    );
}

You should remember to complete and unsubscribe from everything when done (to avoid memory leaks). The call from the component looks the same

Sign up to request clarification or add additional context in comments.

6 Comments

Interesting, I haven’t thought of that. How would I pass the error case to the subject/component though, if the user wants to give up?
you can either this.tempSubject.error or this.tempSubject.complete - depends on how you want to proceed (in my example, in the else section when confirmed is false)
Right, I forgot about that! Why have you made the tempSubject a class property though? You never really use it. Of course it could be used to prevent multiple requests (by returning it if it is defined already).
yes, what you suggest works, and it was infrastructure to add more functionally
If my answer helped you, kindly mark it as accepted
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.