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

Handling angular resolver errors

Suggested Videos
Part 64 - Angular client server architecture | Text | Slides
Part 65 - Angular httpclient get example | Text | Slides
Part 66 - Angular httpclient error handling | Text | Slides

In this video we will discuss handling errors and displaying meaningful error messages to the user, when there is a resolver in between the Angular component and an Angular Service.


A resolver service is usually used to pre-fetch data for a route, before activating that route. When there is a resolver service, between a component and an angular service and if either the angular service or the resolver service throws an error, that target route will not  be activated at all and you will stay on the current route.


Now if your requirement is to navigate the user to that target route and then display a meaningful error message to the user. The trick to this is to create a custom type which contains 2 things
  • The data that you want to return when there is no error
  • The error message that you want to display to the user if there is an error
As you can see in the custom type below we have 2 public properties
  • employeeList property holds the array of employees
  • error property holds the error message to display if there is an error
  • Notice we have defaulted error to NULL
import { Employee } from '../models/employee.model';

export class ResolvedEmployeeList {
    constructor(public employeeList: Employee[], public error: any = null) { }
}

In the resolver service modify the code as shown below.

import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { Injectable } from '@angular/core';
import { EmployeeService } from './employee.service';
import { ResolvedEmployeeList } from './resolved-employeelist.model';

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

@Injectable()
export class EmployeeListResolverService implements Resolve<ResolvedEmployeeList> {
    constructor(private _employeeService: EmployeeService) {
    }

    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<ResolvedEmployeeList> {
        return this._employeeService.getEmployees()
            .pipe(
            map((employeeList) => new ResolvedEmployeeList(employeeList)),
            catchError((err: any) => Observable.of(new ResolvedEmployeeList(null, err)))
            );
    }
}

Code explanation :
  • In either cases we are returning our custom type - ResolvedEmployeeList
  • If the resolver completes successfully, we populate the employeeList property of our custom type with the array of employees we get from the service. 
  • If there is an error, we populate the error property of our custom type, and a value of null is pass to the employeeList property.
  • In the target component where this reolver data is consume check if the error property is NULL or NOT and react accordingly.
In list-employees.component.ts file make the following changes

const resolvedEmployeeList: ResolvedEmployeeList
                    = this._route.snapshot.data['employeeList'];

if (resolvedEmployeeList.error == null) {
  this.employees = resolvedEmployeeList.employeeList;
} else {
  this.error = resolvedEmployeeList.error;
}

Code explanation :
  • If resolvedEmployeeList.error si NULL, then we know the service call completed without errors. So we set employees property of the component to the list of employees we have in employeeList property of resolvedEmployeeList object.
  • If resolvedEmployeeList.error is NOT NULL, then we know there is an error.
In the view template, display the error

<div *ngIf="error">
    {{ error }}
</div>

If you do not want to create a separate type just for handling resolver errors, you may modify the code in the Resolver Service as shown below.

@Injectable()
// The resolver returns a union type - either an Employee[] or string
// Employee[]  will be returned if the resolver completes successfully
// else the string error message will be returned
export class EmployeeListResolverService implements Resolve<Employee[] | string> {
    constructor(private _employeeService: EmployeeService) {
    }

    // The return type of the resolve() method matches with the above
    // Resolve interface signtaure
    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Employee[] | string> {
        return this._employeeService.getEmployees()
            .pipe(catchError((err: string) => Observable.of(err)));
    }
}

Modify the code in list-employees.component.ts file as shown below.

// resolvedData can either be a string or Employee[]
const resolvedData: string | Employee[] = this._route.snapshot.data['employeeList'];

// If the resolver completed without errors resolvedData is an Employee[]
if (Array.isArray(resolvedData)) {
  this.employees = resolvedData;
} else {
  this.error = resolvedData;
}

angular crud tutorial

3 comments:

  1. If you are using angular 5 above
    Replace this line catchError((err: any) => Observable.of(new ResolvedEmployeeList(null, err)))

    with this line
    catchError((err: any) => of(new ResolvedEmployeeList(null, err)))

    and also import this statement
    import { of } from 'rxjs';

    ReplyDelete
  2. resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable {
    return this._employeeService.getEmployees()
    .pipe(
    map((empList) => new ResolvedEmployeeList(empList)),
    catchError((err: any) => of(new ResolvedEmployeeList(null, err)))
    );
    }

    ReplyDelete
  3. //for second method
    .pipe(catchError((err: string) => of(err)));

    ReplyDelete

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