Search code examples
javascriptangularrxjsreactiveangular-httpclient

Stop Angular http Observable stream closing on error


I have a Subject (this.searchSubject) which I call next() on when I want to perform a search:

this.searchSubject.next(postBody)

This causes the following to fire:

this.searchSubject
      .switchMap((view: any) => {
         // upsert here records the search - needed
         return this.searchHistoryService.upsert(view)
          .flatMap(() => this.http.post(this.url, view))
          .map((data) => this.pageTransform(data));
      })
      .subscribe(res => {
        this.successSubject.next(this.pageTransform(res));
      }, err => {
        this.errorSub.next(err);
      });

Unfortunately, no matter what I do, I can't seem to keep the stream alive if this.errorSub.next(err); is called (the error condition).

Since the httpClient (this.http.post) returns a new observable each I wouldnt have thought handling the error would be an issue, but it seems this removes all observers from this.searchSubject.

Note I have a httpInterceptor which returns a thrown error for every error returned.

This must be an extremely common pattern, so what am I doing wrong?


Solution

  • Your error handler is in the outer switchMap projection, thus it will close the outer stream in case of an error. You'll have to move it inside to your switchMap to keep the outer stream alive.

    And since you're using [email protected] you can use the lettable operators which might make it easier to see where to put your error handlers.

    this.searchSubject.pipe(
      switchMap((view: any) => {
       return this.searchHistoryService.upsert(view)
        .pipe(
          flatMap(() => this.http.post(this.url, view)),
          map((data) => this.pageTransform(data)),
          catchError(() => {
            this.errorSub.next(err); 
          })
        );
      })
    ).subscribe(() => {
      this.successSubject.next(this.pageTransform(res));
    });
    

    An important note if you switch to the lettable operators is that you have to import them from rxjs/operators like import { map, catchError } from 'rxjs/operators'; .

    If you stay with the old syntax I think it will be as easy as to add a .catch after your .map() projection.