Aakash Goplani
Posted on February 6, 2022
Angular errors can be broadly classified into two types:
- HTTP Errors
- 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
.
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
So any such errors that are induced by developers comes under Client (Browser) Error category.
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']);
}
}
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){}
This will throw error:
Error: NG0200: Circular Dependency in DI detected for ErrorHandler
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']);
}
}
This solves one problem but leads to another.
Navigation triggered outside Angular zone, did you forget to call
ngZone.run()
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);
}
}
Once we had achieved this, we need to provide this service to root module, example AppModule:
@NgModule({
providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
})
class MyModule {}
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
}
}
Posted on February 6, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.