atultherajput
atultherajput

Reputation: 175

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.

Upvotes: 1

Views: 4764

Answers (2)

j3ff
j3ff

Reputation: 6089

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

Upvotes: 3

Sunil Johnson
Sunil Johnson

Reputation: 1169

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;
})

Upvotes: 1

Related Questions