DEV Community

Aakash Goplani
Aakash Goplani

Posted on • Originally published at blog.aakashgoplani.in

A simple approach towards handling errors in Angular

Angular errors can be broadly classified into two types:

  1. HTTP Errors
  2. Client Errors

HTTP errors occurs whenever we deal with external APIs example, we made call to an endpoint and the network went down or while making the call server was not able to process the request properly and in return sends back error etc. All such scenarios which involve server's response status of 5xx and 4xx role comes under this category. In Angular, they are identified by HttpErrorResponse.

image

Client (Browser) Error is the error that mostly occurs at runtime because of developers mistake while writing the code. Types of these errors are: EvalError, InternalError, RangeError, ReferenceError, SyntaxError, URIError, TypeError. One such example:

(windows as any).abc.pqr = '';
// here property `abc` is not defined on global window object so
// `(windows as any).abc` will result into undefined and
// undefined.pqr will throw TypeError: stating that we are
// trying to set something on a property that does not exists
Enter fullscreen mode Exit fullscreen mode

So any such errors that are induced by developers comes under Client (Browser) Error category.

image

Under both the circumstances its the end user that suffer the most. Whenever any such error occurs, execution of JavaScript stops and the screen freezes giving end user a bad experience. So, the good practice is to handle such errors and perform a relevant action like routing users to error page and displaying some custom message like Something Went Wrong! Please try again later!

Angular comes up with class ErrorHandler that provides default method handleError(error: Error) which we can utilize to catch those errors.

@Injectable()
class MyErrorHandler implements ErrorHandler {
  handleError(error: Error) {
    // do something with the exception like router.navigate(['/error-page']);
  }
}
Enter fullscreen mode Exit fullscreen mode

We can use handleError(error: Error) to catch the error and redirect user to a generic error-page. One problem here is how do we inject the helper service in our custom ErrorHandler implementation?

If we inject the service as we generally do

constructor(private router: Router){}
Enter fullscreen mode Exit fullscreen mode

This will throw error:

Error: NG0200: Circular Dependency in DI detected for ErrorHandler
image

Angular creates ErrorHandler before providers otherwise it won't be able to catch errors that occurs in early phase of application. Hence the providers will not be available to ErrorHandler. So, we need to inject dependent services using injectors.

@Injectable()
class MyErrorHandler implements ErrorHandler {
  constructor(private injector: Injector){}

  handleError(error: Error) {
    const router = this.injector.get(Router);
    router.navigate(['/error-page']);
  }
}
Enter fullscreen mode Exit fullscreen mode

This solves one problem but leads to another.

Navigation triggered outside Angular zone, did you forget to call ngZone.run()
image

The problem here is exactly same as before while injecting our helper services. ErrorHandle runs outside of regular ngZone. So, the navigation should take place outside of the zone so that regular flow of Change Detection is not hampered

@Injectable()
class MyErrorHandler implements ErrorHandler {
  constructor(
    private injector: Injector,
    private zone: NgZone,
  ){}

  handleError(error: Error) {
    const router = this.injector.get(Router);
    this.zone.run(() => router.navigate(['/error-page']));
    console.error('Error Caught: ', error);
  }
}
Enter fullscreen mode Exit fullscreen mode

Once we had achieved this, we need to provide this service to root module, example AppModule:

@NgModule({
  providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
})
class MyModule {}
Enter fullscreen mode Exit fullscreen mode

We can add more customization to above handleError method

handleError(error: Error) {
  if (error instanceof HttpErrorResponse) {
    // HTTP related error
  } else if (error instanceof TypeError || error instanceof ReferenceError) {
    // Runtime exceptions mostly induced by Developer's code
  } else {
    // catch-all: catch rest of errors
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)