docs/en/Community-Articles/2024-02-12-Global-Error-Handling-in-Angular/POST.md
Error handling is how we deal with errors that go wrong when we are running a program. There is no code that runs perfectly forever :) Things can go wrong and your application might crash. So, in order to run your program smoothly you must handle errors. It is just not for keeping your application in a running state. It is also useful to show messages about the error to the client. Like what went wrong, why it is not allowed to access this page etc.
({} as any).doSomething()
try {
({} as any).doSomething();
} catch (error) {
this.toastService.showError(error.message);
}
ErrorHandler interface.import { ErrorHandler, Injectable, inject } from '@angular/core';
import { ToastService } from './toast.service';
@Injectable({
providedIn: 'root'
})
export class CustomErrorHandlerService implements ErrorHandler {
toastService = inject(ToastService);
//This method comes from interface
handleError(error: any): void {
this.toastService.showError(error.message);
}
}
ErrorHandler class from @angular/core.import { ErrorHandler } from '@angular/core';
providers: [
{ provide: ErrorHandler, useExisting: CustomErrorHandlerService }
]
CustomErrorHandlerService won't catch the errors.Make an HTTP request and check if it's working.
As you can see it doesnβt work. So how can we catch the http errors? with catchError() operator in rxjs or observer object. I will go with catchError() operator.
getTodo(id: number) {
this.http
.get(`https://jsonplaceholder.typicode.com/todos/${id}`)
.pipe(catchError((err) => {
this.toastService.showError(err.message);
return EMPTY;
})
)
.subscribe(todo => this.todo = todo);
}
catchError() operator to the entire http requests? NO, we will use HTTP Interceptors!getTodo(id: number) {
this.http.get('https://jsonplaceholder.typicode.com/todos/${id}').subscribe(todo => this.todo = todo);
}
import { Injectable, inject } from '@angular/core';
import { HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { EMPTY, catchError } from 'rxjs';
import { ToastService } from './toast.service';
@Injectable({
providedIn: 'root'
})
export class ToastInterceptor implements HttpInterceptor {
toastService = inject(ToastService);
intercept(req: HttpRequest<any>, next: HttpHandler) {
return next.handle(req).pipe(catchError((error) => {
this.toastService.showError(error.message);
return EMPTY;
}));
}
}
import { HTTP_INTERCEPTORS} from '@angular/common/http';
providers: [
{ provide: HTTP_INTERCEPTORS, useExisting: ToastInterceptor, multi: true }
]
Now everything has set up. Let's make an HTTP request and try again.
HttpErrorReporterService. This service is going to store HttpError in a subject, and share the httpError as an observable for subscribers.RestService which is a layer on top of HttpClient, this new service is able to get a skipHandleError parameter. If skipHandleError value is false then it will be reported to the HttpErrorReporterService otherwise error will be throwed.ErrorHandler, This service is going to subscribe to observable in HttpErrorReporterService and handle the errors (in our case we will show toast message).You can copy the source codes from ABP or use ABP directly π Lets simulate the solution in our application, this simulation is not suitable for your application it just for demonstration.
Rest Service
import { HttpClient, HttpRequest } from '@angular/common/http';
import { Injectable, inject } from '@angular/core';
import { HttpErrorReporterService } from './http-error-reporter.service';
import { catchError, throwError } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class RestService {
http = inject(HttpClient);
httpErrorReporterService = inject(HttpErrorReporterService);
request(req: HttpRequest<any> | { method: string, url: string; }, skipHandleError = false) {
const { method, url } = req;
return this.http.request(method, url).pipe(catchError((err) => {
if (!skipHandleError) {
this.httpErrorReporterService.reportError(err);
}
return throwError(() => err);
}));
}
}
HttpErrorReporterService
import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class HttpErrorReporterService {
private _error$ = new Subject<HttpErrorResponse>();
get error$() {
return this._error$.asObservable();
}
reportError(error: HttpErrorResponse) {
this._error$.next(error);
}
}
ErrorHandler
import { Injectable, inject } from '@angular/core';
import { HttpErrorReporterService } from './http-error-reporter.service';
import { ToastService } from './toast.service';
@Injectable({
providedIn: 'root'
})
export class ErrorHandlerService {
httpErrorReporterService = inject(HttpErrorReporterService);
toastMessageService = inject(ToastService);
constructor(){
this.httpErrorReporterService.error$.subscribe((error) => {
this.toastMessageService.showError(error.message);
});
}
}
Now lets make an http request to check is it working
restService = inject(RestService);
getTodo() {
this.restService.request(
{ method: 'GET', url: 'https://jsonplaceholder.typicode.com/todos/1111' },
).subscribe(todo => {
this.todo = todo;
});
}
Now let's pass true to the skipHandleError parameter, let's see if the errorHandler going to skip this error or not.
restService = inject(RestService);
getTodo() {
this.restService.request(
{ method: 'GET', url: '<https://jsonplaceholder.typicode.com/todos/1111>' },
skipHandleError: true,
).subscribe(todo => {
this.todo = todo;
});
}
Thanks for reading, if you have any advice please share it with me in the comments.