thenolin
thenolin

Reputation: 1064

Http Error Handler Interceptor configuration w/ rxjs Replay Subject - Angular

I'm having an issue where I'm attempting to get the value of an error and display a custom error message via a html element. I'm using a Replay Subject to store the error and manage the state of the error in my Interceptor. I'm importing the interceptor correctly into the component and providing it in my module correctly. I'm confused as to why my error doesn't show up when getting an error response from my API.

Here's what I have so far

errorHandlerInterceptor.service.ts

export class ErrorHandlerInterceptor implements HttpInterceptor {

  error: ReplaySubject<string> = new ReplaySubject<string>(1);

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request)
      .pipe(
        retry(1),
        catchError((error: HttpErrorResponse) => {
          let errorMessage = '';
          if (error.error instanceof ErrorEvent) {
            // client-side error
            errorMessage = `Error: ${error.error.message}`;
          } else {
            // server-side error
            errorMessage = `Error Code: ${error.status}`;
          }
          //window.alert(errorMessage);

          this.error.next(errorMessage);

          return throwError(this.error);
        })
      )
  }
}

Then in the html, I async pipe the Replay Subject to only show the error message if the Replay Subject has a value.

component.html

<div
    *ngIf="errorHandlerInterceptor.error | async"
    class="app-alert--error"
  >
    <p class="app-alert__title">
      ERROR
    </p>
    <p id="error-message">
      {{ errorHandlerInterceptor.error | async }}
    </p>
</div>

Any help or suggestions are greatly appreciated!

Upvotes: 0

Views: 537

Answers (1)

Andrei Gătej
Andrei Gătej

Reputation: 11979

Importing the interceptor inside a component does not look like a good pattern to me.

A way to solve this might be to use a service that will propagate the error to its consumers. This way, from my perspective, we can achieve a better separation of concerns.

error.service.ts

@Injectable({
    providedIn: 'root'
})
export class ErrorService {
    private errorStore = new BehaviorSubject(null);
    public error$ = this.errorStore.asObservable();

    emitError (err) {
        this.errorStore.next(err);
    }
}

foo.component.ts

@Component{/* ... */}
export class FooComponent {

    get currentError$ () {
        return this.errorService.error$;
    }

    constructor (private errorService: ErrorService) { }
}

foo.component.html

<!-- ... -->

<div *ngIf="currentError$ | async">
    Error!
</div>

<!-- ... -->

error.interceptor.ts

constructor (private errorService: ErrorService) { }

intercept (/* ... */) {
    return next.handle(req)
        .pipe(
            /* ... */
            catchError(err => {
                /* ... */

                // If error found
                this.errorService.emitError(errorMessage);

                /* ... */
            }),
        )
}

Upvotes: 1

Related Questions