Angular 2 already has a very good error handler. When an error randomly occurs in your code, the Angular’s error handler will catch it and will print the error details in the console. The error details will also include the line number with a link to the source file. In most cases this should be more then enough to help you understand what is happening in your application.
Why would I need a custom error handler?
You can use a custom error handler to format the error messages that are logged into the browser’s console, to catch custom business errors in your application (Authentication/Authorization errors, HTTP 404, etc.) or maybe you want to also send the errors to you backend server for analytics or other reasons. All of the above are valid use cases of using a custom error handler.
Creating a custom error handler
Writing your own error handler is straightforward. You will need to extend Angular’s ErrorHandler
class and override the handleError
method. This method receives a wrapper of the original error as a parameter. You can find the original error in the error.originalError
property. The default implementation uses console.error()
to print the error details in the browser’s console.
CustomErrorHandler.ts
import {ErrorHandler} from '@angular/core'; export class CustomErrorHandler extends ErrorHandler { constructor(){ super(false); } public handleError(error: any): void { // You can add your own logic here. // It is not required to delegate to the original implementation super.handleError(error); } }
Extending Angular’s Error Handler
If you want to use your own custom handler you will need to somehow replace the one that Angular is using. Fortunately enough, Angular’s awesome DI engine will allows you to provide custom implementations for different classes.
AppModule.ts:
import {BrowserModule} from '@angular/platform-browser'; import {ErrorHandler} from '@angular/core'; import {AppComponent} from './app.component'; import {CustomErrorHandler} from './CustomErrorHandler'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule ], bootstrap: [ AppComponent ], providers: [ {provide: ErrorHandler, useClass: CustomErrorHandler} ] }) export class AppModule {}
In the code above take a look at the providers
field in the @NGModule
decorator. With the above configuration in place, every time Angular’s DI engine will request an instance of ErrorHandler
will receive an instance of our custom implementation CustomErrorHandler
.
Creating custom error classes
Sometimes it’s useful to create custom error classes that you can throw in different parts of your application. For example, when the user is doesn’t have access to a specific part of the application you can throw a custom AuthorizationError.
Creating the custom error class
AuthorizationError.ts:
export class AuthorizationError { toString() { return 'You are not authorized to view this content!!!'; } }
Updating the CustomErrorHandler class
CustomErrorHandler.ts:
import {ErrorHandler} from '@angular/core'; import {AuthorizationError} from './AuthorizationError'; export class CustomErrorHandler extends ErrorHandler { constructor(){ super(false); } public handleError(error: any): void { if(error.originalError instanceof AuthorizationError){ console.info(`[CUSTOM ERROR]:::${error.originalError.toString()}`); } else { super.handleError(error); } } }
Using the new error in your code
app.component.html:
<button (click)="throwCustomError()">Throw Authorization Error</button>
app.component.ts:
import {Component} from '@angular/core'; import {AuthorizationError} from './AuthorizationError'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { throwCustomError() { throw new AuthorizationError(); } }
Now when you press the Throw Authorization Error
button you will see our custom error logging in the browser’s console:
[CUSTOM ERROR]:::You are not authorized to view this content!!!
If you look closely at the CustomErrorHandler
class, everything that is not of type AuthorizationError
will be delegated to the original Angular’s ErrorHandler.
I tried implementing this example and for some reason this condition is always false for me, (error.originalError instanceof AuthorizationError). I am not sure what I could be missing.
LikeLike
Actually never mind, it was some setup issue in the project I was working. This works! Thanks for the post though, it was very helpful.
LikeLiked by 1 person
Tara, I have the same issue (false always). Can you suggest how to resolve this?
LikeLike
Tara, I have the same issue (false always). Can you suggest how to resolve this?
LikeLike
Do you know how I can show the error in a template instead of in a console log? I tried:
import { Component, ErrorHandler, OnInit } from ‘@angular/core’;
import { GenericError } from ‘./generic-error.component’;
@Component({
selector: ‘custom-error-handler’,
templateUrl: ‘app/error-handler/custom-error-handler.component.html?’ + +new Date()
})
export class CustomErrorHandler extends ErrorHandler {
errorText: string;
constructor() {
super(false);
}
ngOnInit() {
this.errorText = ‘Initial text!’;
}
public handleError(error: any): void {
if (error.originalError instanceof GenericError) {
console.info(‘This is printed to console!’);
this.errorText = “I want it to print this in the template!”;
}
else {
super.handleError(error);
}
}
}
My template contains this:
{{errorText}}
When I start my site, I shows “Initial text!” in the template view.
When I then throw an exception it shows prints “This is printed to console!” in the console window, but the template view is not updated with “I want it to print this in the template!”.
Do you know why it can print to console but not access the template when inside handleError?
LikeLike
I would redirect to an error route with the actual error as a parameter. The error route will have a template and a component that would use the ActivatedRoute.data to retrieve the error parameter and print it.
LikeLike
I need to inject loggerService and router to customErrorHandler. Is it possible? How?
LikeLike
@Nitu, try something like this:
import { ErrorHandler, forwardRef, Injector, Inject } from ‘@angular/core’;
constructor(
@Inject(forwardRef(() => Injector))
private injector: Injector) {}
Then in your method, this.injector.get(_your type here_)
LikeLike
thank you..
LikeLike
[…] From this post […]
LikeLike
Why is there a parameter in the super function in the constructor?
Also, there is no constructor in the ErrorHandler implementation: https://github.com/angular/angular/blob/5.2.10/packages/core/src/error_handler.ts#L10-L80
So i completely dont understand for what it is good:
constructor(){
super(false);
}
would be nice if someone can explain. I dont like copy&paste things i dont understand 🙂
Thanks Brandy
LikeLike
[…] Ho provato a creare un custom globale ErrorHandler e seguito le istruzioni dettagliate qui […]
LikeLike
[…] J'ai essayé de créer un personnalisé, global ErrorHandler et suivi les instructions détaillées ici […]
LikeLike
[…] I tried to create a custom global ErrorHandler and followed the instruction detailed here […]
LikeLike
[…] I tried to create a custom global ErrorHandler and followed the instruction detailed here […]
LikeLike
[…] I tried to create a custom global ErrorHandler and followed the instruction detailed here […]
LikeLike