Angular Custom Error Handler
May 05, 2018
This page will walk through Angular custom error handler. Angular provides ErrorHandler
class for centralized exception handling. We create custom error handler service by implementing ErrorHandler
class. It has a method handleError(error)
that executes whenever the application throws error anywhere in the application. We need to override it to define our custom error handler. We can use our custom error handler as global error handler. ErrorHandler
is created before the providers. So we need Injector
to get instance of any service within custom error handler. If our application is using routing then to get URL in custom error handler, we should use Router
otherwise we can use Location
. We can navigate to a global error page for a user friendly message after handling error in our custom error handler service. Here we will provide complete example to create custom error handler step by step.
Contents
Technologies Used
Find the technologies being used in our example.1. Angular 6.0.0
2. Angular CLI 6.0.0
3. TypeScript 2.7.2
4. Node.js 10.0.0
5. NPM 6.0.0
ErrorHandler
AngularErrorHandler
class is used for centralized exception handling. The default implementation of ErrorHandler
prints error messages to browser console. We implement this class to create custom error handler. ErrorHandler
has a method handleError(error)
that executes whenever the application throws error anywhere in the application. Find the structure of ErrorHandler
class from Angular Doc.
class ErrorHandler { handleError(error: any): void }
ErrorHandler
class.
Steps to Create Custom Error Handler
Here we will discuss to create custom error handler usingErrorHandler
.
Step-1: Create a service by implementing
ErrorHandler
and overriding its handleError()
method. Here we are creating GlobalErrorHandlerService
as custom error handler.
import { Injectable, ErrorHandler } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; @Injectable() export class GlobalErrorHandlerService implements ErrorHandler { handleError(error: any) { if (error instanceof HttpErrorResponse) { //Backend returns unsuccessful response codes such as 404, 500 etc. console.error('Backend returned status code: ', error.status); console.error('Response body:', error.message); } else { //A client-side or network error occurred. console.error('An error occurred:', error.message); } } }
GlobalErrorHandlerService
in application module in providers
metadata of @NgModule
as following.
@NgModule({ ------ providers: [ GlobalErrorHandlerService, { provide: ErrorHandler, useClass: GlobalErrorHandlerService }, ----- ] }) export class AppModule { }
ErrorHandler
will not be called by application. When an error will be thrown from anywhere in the application, our handleError()
method of GlobalErrorHandlerService
will execute.
Step-3: Error will be thrown in application automatically or by using
throw
keyword. In both case GlobalErrorHandlerService
will execute. Find the first case.
this.personService.addPerson(person) .subscribe(data => { console.log(data); } );
handleError()
method of GlobalErrorHandlerService
will execute. Now find the second case.
this.countryService.addCountry(country) .subscribe(data => { console.log(data); }, err => { throw err; } );
throw
keyword and then handleError()
method of GlobalErrorHandlerService
will execute.
Using Injector
ErrorHandler
is created before the providers. So we need Injector
for dependency injection in our custom error handler class. Injector
is imported from @angular/core
library. Find the code snippet to use Injector
.
@Injectable() export class GlobalErrorHandlerService implements ErrorHandler { constructor(private injector: Injector) { } handleError(error: any) { let router = this.injector.get(Router); console.log('URL: ' + router.url); ------ } }
Router
using Injector
.
Get URL using Router and Location
When an exception is thrown globally, we are interested to know the URL for which exception is thrown. If our application is using routing then to get URL in custom error handler, we should useRouter
otherwise we can use Location
. Find the code snippet to get URL using Router
.
@Injectable() export class GlobalErrorHandlerService implements ErrorHandler { constructor(private injector: Injector) { } handleError(error: any) { let router = this.injector.get(Router); console.log('URL: ' + router.url); ------ } }
Router
is imported from @angular/router
library. If our application is not using routing, we can use Location
to get URL as following.
@Injectable() export class GlobalErrorHandlerService implements ErrorHandler { constructor(private injector: Injector, private location: Location) { } handleError(error: any) { console.log(this.location.path()); ------ } }
Location
, LocationStrategy
and PathLocationStrategy
in application module using providers as following.
providers: [ Location, {provide: LocationStrategy, useClass: PathLocationStrategy} ]
Location
, LocationStrategy
and PathLocationStrategy
are imported from @angular/common
library.
Using Logger Service
We can use a logger service to log error. Suppose we have aLoggerService
as following.
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class LoggerService { log(error: any) { console.log(error.message); } }
LoggerService
instance using Injector
.
@Injectable() export class GlobalErrorHandlerService implements ErrorHandler { constructor(private injector: Injector) { } handleError(error: any) { let router = this.injector.get(Router); console.log('URL: ' + router.url); let loggerService = this.injector.get(LoggerService); loggerService.log(error); } }
Global Error Handler
Creating customErrorHandler
works as global error handler because our custom ErrorHandler
will execute for any error thrown from anywhere in the application. We can redirect from custom error handler service to global error handler page to show a user friendly message. Suppose we have a global error component as following.
import { Component } from '@angular/core'; @Component({ template: ` <h2>An error occurred.</h2> ` }) export class GlobalErrorComponent { }
const routes: Routes = [ ------ { path: 'error', component: GlobalErrorComponent } ];
import { Injectable, ErrorHandler, Injector } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; import { Router } from '@angular/router'; @Injectable() export class GlobalErrorHandlerService implements ErrorHandler { constructor(private injector: Injector) { } handleError(error: any) { let router = this.injector.get(Router); console.log('URL: ' + router.url); if (error instanceof HttpErrorResponse) { //Backend returns unsuccessful response codes such as 404, 500 etc. console.error('Backend returned status code: ', error.status); console.error('Response body:', error.message); } else { //A client-side or network error occurred. console.error('An error occurred:', error.message); } router.navigate(['/error']); } }
Complete Example
Find the project structure.my-app | |--src | | | |--app | | | | | |--country | | | | | | | |--add-country.component.html | | | |--add-country.component.ts | | | |--country.service.ts | | | |--country.ts | | | | | |--person | | | | | | | |--add-person.component.html | | | |--add-person.component.ts | | | |--person.service.ts | | | |--person.ts | | | | | | | | |--global-error-handler.service.ts | | |--global-error.component.ts | | |--page-not-found.component.ts | | |--app-routing.module.ts | | |--app.component.ts | | |--app.module.ts | | | |--main.ts | |--index.html | |--styles.css | |--node_modules |--package.json
global-error-handler.service.ts
import { Injectable, ErrorHandler, Injector } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; import { Router } from '@angular/router'; @Injectable() export class GlobalErrorHandlerService implements ErrorHandler { constructor(private injector: Injector) { } handleError(error: any) { let router = this.injector.get(Router); console.log('URL: ' + router.url); if (error instanceof HttpErrorResponse) { //Backend returns unsuccessful response codes such as 404, 500 etc. console.error('Backend returned status code: ', error.status); console.error('Response body:', error.message); } else { //A client-side or network error occurred. console.error('An error occurred:', error.message); } router.navigate(['/error']); } }
import { Component } from '@angular/core'; @Component({ template: ` <h2>An error occurred.</h2> ` }) export class GlobalErrorComponent { }
import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { AddCountryComponent } from './country/add-country.component'; import { AddPersonComponent } from './person/add-person.component'; import { GlobalErrorComponent } from './global-error.component'; import { PageNotFoundComponent } from './page-not-found.component'; const routes: Routes = [ { path: 'country', component: AddCountryComponent }, { path: 'person', component: AddPersonComponent }, { path: 'error', component: GlobalErrorComponent }, { path: '', redirectTo: '/country', pathMatch: 'full' }, { path: '**', component: PageNotFoundComponent } ]; @NgModule({ imports: [ RouterModule.forRoot(routes) ], exports: [ RouterModule ] }) export class AppRoutingModule{ }
import { NgModule, ErrorHandler } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { ReactiveFormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; import { AddCountryComponent } from './country/add-country.component'; import { AddPersonComponent } from './person/add-person.component'; import { GlobalErrorComponent } from './global-error.component'; import { PageNotFoundComponent } from './page-not-found.component'; import { AppRoutingModule } from './app-routing.module'; import { GlobalErrorHandlerService } from './global-error-handler.service'; @NgModule({ imports: [ BrowserModule, ReactiveFormsModule, HttpClientModule, AppRoutingModule ], declarations: [ AppComponent, AddCountryComponent, AddPersonComponent, GlobalErrorComponent, PageNotFoundComponent ], providers: [ GlobalErrorHandlerService, { provide: ErrorHandler, useClass: GlobalErrorHandlerService } ], bootstrap: [ AppComponent ] }) export class AppModule { }
<h3>Add Country</h3> <form [formGroup]="countryForm" (ngSubmit)="onFormSubmit()"> <p> Name: <input formControlName="name"> </p> <p> Capital: <input formControlName="capital"> </p> <p> Currency: <input formControlName="currency"> </p> <p> <button>Add</button> </p> </form>
import { Component } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { CountryService } from './country.service'; import { Country } from './country'; @Component({ templateUrl: './add-country.component.html' }) export class AddCountryComponent { constructor(private countryService: CountryService) { } countryForm = new FormGroup({ name: new FormControl(), capital: new FormControl(), currency: new FormControl() }); onFormSubmit() { let country = this.countryForm.value; this.countryService.addCountry(country) .subscribe(data => { console.log(data); }, err => { throw err; } ); } }
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { Country } from './country'; @Injectable({ providedIn: 'root' }) export class CountryService { url = "/api/countries"; constructor(private http: HttpClient) { } addCountry(country: Country): Observable<Country> { return this.http.post<Country>(this.url, country); } }
export interface Country { name:string; capital:string; currency:string; }
<h3>Add Person</h3> <form [formGroup]="personForm" (ngSubmit)="onFormSubmit()"> <p> Name: <input formControlName="name"> </p> <p> City: <input formControlName="city"> </p> <p> Mobile: <input formControlName="mobile"> </p> <p> <button>Add</button> </p> </form>
import { Component } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { PersonService } from './person.service'; import { Person } from './person'; @Component({ templateUrl: './add-person.component.html' }) export class AddPersonComponent { constructor(private personService: PersonService) { } personForm = new FormGroup({ name: new FormControl(), city: new FormControl(), mobile: new FormControl() }); onFormSubmit() { let person = this.personForm.value; this.personService.addPerson(person) .subscribe(data => { console.log(data); } ); } }
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { Person } from './person'; @Injectable({ providedIn: 'root' }) export class PersonService { url = "/api/persons"; constructor(private http: HttpClient) { } addPerson(person: Person): Observable<Person> { return this.http.post<Person>(this.url, person); } }
export interface Person { name:string; city:string; mobile:string; }
import { Component } from '@angular/core'; import { Location } from '@angular/common'; @Component({ template: `<h2>Page Not Found.</h2> <div> <button (click)="goBack()">Go Back</button> </div> ` }) export class PageNotFoundComponent { constructor(private location: Location) { } goBack(): void { this.location.back(); } }
import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: ` <nav ngClass = "parent-menu"> <ul> <li><a routerLink="/country" routerLinkActive="active">Country</a></li> <li><a routerLink="/person" routerLinkActive="active">Person</a></li> </ul> </nav> <div ngClass = "parent-container"> <router-outlet></router-outlet> </div> ` }) export class AppComponent { }
.parent-menu ul { list-style-type: none; margin: 0; padding: 0; overflow: hidden; background-color: #333; } .parent-menu li { float: left; } .parent-menu li a { display: block; color: white; text-align: center; padding: 15px 15px; text-decoration: none; } .parent-menu li a:hover:not(.active) { background-color: #111; } .parent-menu .active{ background-color: #4CAF50; } .parent-container { padding-left: 10px; }
Run Application
To run the application, find the steps.1. Download source code using download link given below on this page.
2. Use downloaded src in your Angular CLI application. To install Angular CLI, find the link.
3. Run ng serve using command prompt.
4. Access the URL http://localhost:4200 and try to add country or person data. We will get error page.
Find the print screen of the output.

References
Angular Doc: ErrorHandlerAngular Routing and Navigation Example