Search code examples
angulartypescripterror-handlingrxjsngrx

Global ErrorHandler is not working after implementing catchError in ngrx effect


I am trying to handle the error in my angular application but global error handler is not working.

I am using ngrx for state management and have a global error handler in my angular application. I am using catchError operator to handle the error in ngrx/effects as suggested here. But now I am not able to use global error handler and I have to catchError in each effect.

//ErrorHandler

handleError(error: Error | HttpErrorResponse) {
  const router = this.injector.get(Router);
  console.error("Error intercepted.", error);
  this.spinner.hide();
  if (error instanceof HttpErrorResponse) {
     if (!navigator.onLine) {
        this.showError('No internet connection');
     } else {
        if (error.error.message) {
            this.showError(`${error.error.message}`);
        } else {
            this.showError(`${error.status} - ${error.message}`);
        }
     }
  } else {
     this.showError("Unknow error.");
     router.navigate(['/dashboard']);
    }
}

//ngrx effects

export class EffectError implements Action {
    readonly type = '[Error] Effect Error';
}

@Effect()
UserAuth: Observable < Action > = this.actions.pipe(
  ofType(SigninActions.AUTHENTICATE_USER),
  switchMap((action: SigninActions.AuthenticateUser) =>
    this.signinService.signin(action.payload.emailAddress, action.payload.password).pipe(
        map((loginContext: LoginContext) => {
            console.log("LOGIN_CONTEXT", loginContext);
            return new SigninActions.AuthenticateUserSuccess(loginContext)
        }),
        //CatchError
        catchError(() => of(new EffectError()))
    )
   )
);

I am using catchError operator so that effect does not get break whenever an error occurs and global error handler to show different error messages.


Solution

  • The problem is that catchError in @Effect will swallow the error and it won't propagate back to the ErrorHandler.

    You can find a lot of posts on how to do this but to summarize, what you should do is implement HttpInterceptor and use catchError to handle the errors sent back by the HttpHandler:

    import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
    import { Injectable } from '@angular/core';
    import { Observable, throwError } from 'rxjs';
    import { catchError } from 'rxjs/operators';
    
    @Injectable()
    export class ErrorInterceptor implements HttpInterceptor {
      intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(request).pipe(
          catchError(error => {
            if (error instanceof HttpErrorResponse) {
              // check for internet connection
              if (!navigator.onLine) {
                this.showError('No internet connection');
              }
              // handle HTTP errors
              switch (error.status) {
                case 401:
                  // do something
                  break;
                case 403:
                  // do something else
                  break;
                default:
                  // default behavior
              }
            }
            // need to rethrow so angular can catch the error
            return throwError(error);
          }));
      }
    }
    

    Then of course, don't forget to provide your implementation of the error interceptor using HTTP_INTERCEPTORS injection token in your AppModule:

    import { HTTP_INTERCEPTORS } from '@angular/common/http';
    
    ...
    
    providers: [
      { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
    ],
    

    This way you can handle errors combining both...

    • ErrorHandler for client-side errors (javascript, angular, ...)
    • HttpInterceptor for HTTP requests

    Here's a good post on different ways to handle errors in angular:
    https://medium.com/angular-in-depth/expecting-the-unexpected-best-practices-for-error-handling-in-angular-21c3662ef9e4