1

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.

atultherajput
  • 175
  • 3
  • 18
  • Since you catch error in ngrx effect, it's not propagated up to the global error handler. What do you want to achieve, why do you expect error to appear in global handler? – Borys Kupar Feb 19 '19 at 12:41
  • I want to show error using global handler. Is there any way to handle error using the error handler without breaking ngrx/effect. – atultherajput Feb 19 '19 at 12:50
  • If you just remove catchError from ngrx effect, what do you mean that it breaks effect? – Borys Kupar Feb 19 '19 at 13:07
  • If I remove catchError then ngrx/effect will stop working after the first error occurs. Check this issue: https://stackoverflow.com/a/41685689/5404825 – atultherajput Feb 19 '19 at 13:14
  • Oh, yes, you are right. But I think since the error was catch in effect there is no way to propagate it to global error handler. Instead I could suggest you, to show error on UI based on your state. Meaning when you dispatch EffectError, this can set "failed" state in reducer -> and based on this you show error message in UI. – Borys Kupar Feb 19 '19 at 13:51

2 Answers2

3

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

Michael Karén
  • 1,105
  • 9
  • 13
j3ff
  • 5,719
  • 8
  • 38
  • 51
1

Assuming your globalerrorhandler is as below :

@Injectable()
export class GlobalErrorHandler extends ErrorHandler {
  public handleError(error: Error | HttpErrorResponse) { }
}

You can throw the error back from the catcherror (in your effects) and your handleError method shall now get called.

example.effect.ts

catchError((err) => {
  const error: string = err.message;
  this.store.dispatch(exampleActions.ResultFailure({ error }));
  throw err;
})
Sunil Johnson
  • 1,059
  • 8
  • 6