Support us .Net Basics C# SQL ASP.NET Aarvi MVC Slides C# Programs Subscribe Download

Angular httpclient error handling

Suggested Videos
Part 63 - Online fake REST API | Text | Slides
Part 64 - Angular client server architecture | Text | Slides
Part 65 - Angular httpclient get example | Text | Slides

In this video we will discuss error handling in Angular. When using HttpClient, to call a server side service, errors may occur. When they do occur we want to handle these errors. 


When an Angular component needs data, there are usually 3 players
  • The Component itself
  • The Angular Service and
  • The Server Side Service

Angular httpclient error handling

The first question that comes to our mind is, should we handle the service related errors in the service itself or in the component that consumes the service. According to Angular style guide, error inspection, interpretation, and resolution is something you want to do in the service, not in the component.

If all goes well the server side service provides data to the client side angular service and the Angular service provides it to the component, which then displays that data to the user.

However, sometimes the request may fail on the server or on the client. There are two types of errors that can occur. 
  • The server may reject the request, returning an HTTP response with a status code such as 404 or 500. These are error responses.
  • Something could go wrong on the client-side such as a network error that prevents the request from completing successfully or an exception thrown in an RxJS operator. These errors produce JavaScript ErrorEvent objects.
  • The HttpClient captures both kinds of errors in its HttpErrorResponse and you can inspect that response to figure out what really happened.
A typical error handler method may look as shown below.

private handleError(errorResponse: HttpErrorResponse) {
    if (errorResponse.error instanceof ErrorEvent) {
        console.error('Client Side Error :', errorResponse.error.message);
    } else {
        console.error('Server Side Error :', errorResponse);
    }
    // return an observable with a meaningful error message to the end user
    return new ErrorObservable('There is a problem with the service.
           We are notified & working on it. Please try again later.');
}

Please do not forget the following imports

import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
  • So, the important point to keep in mind is, if the HttpErrorResponse is an instance of ErrorEvent, then it means that a client-side or network error occurred. If it's not an instance of ErrorEvent, then it means a server error occurred.
  • In a real world application, we may log the errors to a database table or a file for debugging and fixing.
  • Notice, in the error hanlder, we are logging the actual errors and returning an ErrorObservable with a user-friendly error message.
  • Consumers of the service expect service methods to return an Observable of some kind, even a "bad" one.
  • Displaying the actual raw errors to the end user is bad for two reasons - First they are cryptic and does not make much sense to the end user and second they may contain sensitive information that could be useful for a potential hacker. That is why we are logging the actual error and returning a user friendly error message.
Finally, take the Observables returned by the HttpClient methods and pipe them through to the error handler as shown below.

getEmployees(): Observable<Employee[]> {
  return this.httpClient.get<Employee[]>('http://localhost:3000/employees1')
      .pipe(catchError(this.handleError));
}

Please do not forget the following imports

import { Observable } from 'rxjs/Observable';
import { catchError } from 'rxjs/operators';

With the release of rxjs version 5.5, we have Pipeable Operators that can be used along with the pipe() function. Before the introduction of pipeable operators, we only had chain operators as shown below. The catch operator in the example below is called a patch operator.

import 'rxjs/add/operator/catch';

getEmployees(): Observable<Employee[]> {
    return this.httpClient.get<Employee[]>('http://localhost:3000/employees1')
        .catch(this.handleError);
}

So the point that I am, trying to make is
  • There are 2 types of operators in rxjs - Pipeable Operators and Patch Operators
  • Pipeable Operators are imported from rxjs/operators/
  • Patch Operators are imported from rxjs/add/operator/
  • Pipeable Operators have several benefits over Patch Operators. So if you have rxjs version 5.5 or later use pipeable operators.
  • Use the following link to read the benefits of pipeable operators
    https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md
In our case, the angular service getEmployees() method is consumed by a Resolver service, and this resolver service provides the data to the LIST route. If there is an exception, the resolver fails and the target route, in our case the LIST route will not be activated. If a component is directly consuming the angular service getEmployees() method, then it is easy to catch the error observable and display the error message to the user. 

However, when a resolver is involved, the target route is not activated if there is an error. So displaying an error message to the end user is a bit more involved.

In our next video, we will discuss handling errors and displaying error messages when there is a resolver between an angular service and the component.

angular crud tutorial

1 comment:

  1. For RxJS 6 and above use below code

    import { Injectable } from '@angular/core';
    import { Employee } from '../models/employee.model';
    import { Observable, throwError } from 'rxjs';
    import { of } from 'rxjs';
    import { delay } from 'rxjs/operators'
    import { HttpClient, HttpErrorResponse } from '@angular/common/http';
    import { catchError } from 'rxjs/operators';

    @Injectable()

    export class EmployeeService {
    private listEmployees: Employee[] = [
    {
    id: 1,
    name: 'Mark',
    gender: 'Male',
    contactPreference: 'Email',
    email: 'mark@pragimtech.com',
    dateOfBirth: new Date('10/25/1988'),
    department: '3',
    isActive: true,
    photoPath: 'assets/images/mark.png'
    },
    {
    id: 2,
    name: 'Mary',
    gender: 'Female',
    contactPreference: 'Phone',
    phoneNumber: 2345978640,
    dateOfBirth: new Date('11/20/1979'),
    department: '2',
    isActive: true,
    photoPath: 'assets/images/mary.png'
    },
    {
    id: 3,
    name: 'John',
    gender: 'Male',
    contactPreference: 'Phone',
    phoneNumber: 5432978640,
    dateOfBirth: new Date('3/25/1976'),
    department: '3',
    isActive: false,
    photoPath: 'assets/images/john.png'
    },
    ];

    constructor(private httpClient: HttpClient) {
    }

    // getEmployees(): Observable {
    // return this.httpClient.get('http://localhost:3000/employees');
    // }

    getEmployees(): Observable {
    return this.httpClient.get('http://localhost:3000/employees')
    .pipe(catchError(this.handleError));
    }

    getEmployee(id: number): Employee {
    return this.listEmployees.find(e => e.id === id);
    }

    save(employee: Employee) {
    if (employee.id === null) {
    const maxId = this.listEmployees.reduce(function (e1, e2) {
    return (e1.id > e2.id) ? e1 : e2;
    }).id;
    employee.id = maxId + 1;

    this.listEmployees.push(employee);
    } else {
    const foundIndex = this.listEmployees.findIndex(e => e.id === employee.id);
    this.listEmployees[foundIndex] = employee;
    }
    }

    deleteEmployee(id: number) {
    const i = this.listEmployees.findIndex(e => e.id === id);
    if (i !== -1) {
    this.listEmployees.splice(i, 1);
    }
    }

    // private handleError(errorResponse: HttpErrorResponse) {
    // if (errorResponse.error instanceof ErrorEvent) {
    // console.error('Client Side Error :', errorResponse.error.message);
    // } else {
    // console.error('Server Side Error :', errorResponse);
    // }
    // // return an observable with a meaningful error message to the end user
    // return new ErrorObservable('There is a problem with the service.
    // We are notified & working on it. Please try again later.');
    // }

    private handleError(errorResponse: HttpErrorResponse) {
    if (errorResponse.error instanceof ErrorEvent) {
    console.error('Client Side Error :', errorResponse.error.message);
    } else {
    console.error('Server Side Error :', errorResponse);
    }
    return throwError('There is a problem with the service. We are notified & working on it. Please try again later.');
    }

    // getEmployees(): Observable {
    // return of(this.listEmployees).pipe(delay(2000));
    // }
    }

    ReplyDelete

It would be great if you can help share these free resources