Reputation: 175
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
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 requestsHere'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
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