How to catch an error in a request, then open the modal code, and then try again when the modal closes using RxJS

I want to call a server that can return an authorization failure (401) with the Angular2 HTTP class.

The request stream should look like this:

  • The user makes a request to the server using myService.getSomething (). subscribe ()
  • If the server returns 401: open a modal window that asks the user for its credentials.
  • User will successfully log into the application
  • Modal closes and calls back
  • The callback should repeat the original request (myService.getSomething (). Subscribe ())

Here is what I have at the moment:

export class MyService { // ... public getSomething(): Observable<Response> { return this.http.get(url, options).catch((res: any, ob: any) => this.errorHandler(res, ob)); } public errorHandler(res: Response, ob: Observable<any>): Observable<Response> { if (res.status === 401) { this.modalService.open(new ModalConfig({ content: LoginModalComponent, close: () => { ob.retry(1); console.log("weow") } // <=close is the callback that should initiate the retry action on the initial request. })); } else { return Observable.throw(res.json()); } } } 

doSomething () is used as follows: doSomething().map((r) => r.json()).subscribe((r) => ....)

Update 1

I modified my code to look like @Thierry Templier's solution.

 private errorHandler(res: Response, ob: Observable<any>): Observable<Response> { if (res.status === 401) { let closedSubject = new Subject(); this.modalService.open(new ModalConfig({ content: LoginModalComponent, close: () => { closedSubject.next(res);} // I also tried .complete(), .next(null), .next(true), .next(false) })); return ob.retryWhen(() => closedSubject); } else { return Observable.throw(res.json()); } } 

Unfortunately, this still does not work. RetryWhen runs immediately and does not wait for closedSubject.next () to be called. Therefore, it starts an infinite loop, dispatching the original Observable function (getSomething () function).

Update 2

I created a plunker to demonstrate an endless loop:

https://plnkr.co/edit/8SzmZlRHvi00OIdA7Bga

Warning: starting the plunker will spam your console using the string "test"

Update 3

Following Thierry's correct answer, I tried to find a way not to use the source field, since it is protected. After you asked about the rxjs tracker problem to make the field open, the participant answered with a better solution.

 public get(url: string, options?: RequestOptionsArgs): Observable<Response> { return super.get(url, options).retryWhen((errors: any) => this.errorHandler(errors)); } private errorHandler(errors): any { return errors.switchMap((err) => { if (err.status === 401) { let closedSubject = new Subject(); this.modalService.open(new ModalConfig({ content: LoginModalComponent, close: () => { closedSubject.next(err); } })); return <any>closedSubject; } else { return Observable.throw(err.json()); } }); } 

I do not use .catch, so I do not need to use the source field.

+7
javascript angular typescript rxjs rxjs5
source share
2 answers

I think you need to return something observable even in case of error 401:

 public errorHandler(res: Response, ob: Observable<any>): Observable<Response> { if (res.status === 401) { let closedSubject = new Subject(); this.modalService.open(new ModalConfig({ content: LoginModalComponent, close: () => { closedSubject.next(); })); return ob.retryWhen(() => closedSubject); } else { return Observable.throw(res.json()); } } 

See this article for more details: https://jaxenter.com/reactive-programming-http-and-angular-2-124560.html .

Edit

The problem is that the second catch callback parameter is not an observable source. This observable source matches the value of its source property:

 return ob.source.retryWhen((errors) => closedSubject); 

See working plunkr: https://plnkr.co/edit/eb2UdF9PSMhf4Dau2hqe?p=preview .

+7
source share

I think retryWhen operator should help.

+2
source share

All Articles