While sending requests to our web API server, we can get an error in response. Therefore, using Angular Error Handling to handle those errors while sending HTTP requests is a must. That’s exactly what we are going to do in this post. If we get the 404 or the 500 error, we are going to redirect the user to a specific page. For other errors, we are going to show an error message in a modal form. The page that handles the 404 error is already created, so, let’s continue on by creating a 500 (Internal server error) component.

If you want to see all the basic instructions and complete navigation for the .NET Core series, check out the following link: Introduction of the .NET Core series.

For the complete navigation and all the basic instructions of the Angular series, check out: Introduction of the Angular series.

The source code is available at GitHub .NET Core, Angular, and MySQL. Part 11 – Source Code

This post is divided into several sections:

Creating the 500 (Internal Server Error) Component

In the error-pages folder, we are going to create a new component by typing the AngularCLI command:

ng g component error-pages/internal-server --skipTests

Let’s modify the internal-server.component.ts:
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-internal-server',
  templateUrl: './internal-server.component.html',
  styleUrls: ['./internal-server.component.css']
})
export class InternalServerComponent implements OnInit {
  public errorMessage: string = "500 SERVER ERROR, CONTACT ADMINISTRATOR!!!!";

  constructor() { }

  ngOnInit() {
  }

}

Then, let’s modify the internal-server.component.html:
<p>
  {{errorMessage}}
</p>

Additionally, we are going to modify the internal-server.component.css as well:
p{
    font-weight: bold;
    font-size: 50px;
    text-align: center;
    color: #c72d2d;
}

Finally, let’s modify the app.module.ts:
RouterModule.forRoot([
      { path: 'home', component: HomeComponent },
      { path: 'owner', loadChildren: () => import('./owner/owner.module').then(m => m.OwnerModule) },
      { path: '404', component: NotFoundComponent},
      { path: '500', component: InternalServerComponent },
      { path: '', redirectTo: '/home', pathMatch: 'full' },
      { path: '**', redirectTo: '/404', pathMatch: 'full'}
    ])

That’s it.

We have created our component and it is time to create a service for error handling.

Creating a Service for Angular Error Handling

In the folder shared/services create a new service and name it error-handler.service.ts:

ng g service shared/services/error-handler --skipTests

Let’s modify that error-handler.service.ts file:

import { Injectable } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { Router } from '@angular/router';

@Injectable({
  providedIn: 'root'
})
export class ErrorHandlerService {
  public errorMessage: string = '';
 
  constructor(private router: Router) { }
 
  public handleError = (error: HttpErrorResponse) => {
    if(error.status === 500){
      this.handle500Error(error);
    }
    else if(error.status === 404){
      this.handle404Error(error)
    }
    else{
      this.handleOtherError(error);
    }
  }
 
  private handle500Error = (error: HttpErrorResponse) => {
    this.createErrorMessage(error);
    this.router.navigate(['/500']);
  }
 
  private handle404Error = (error: HttpErrorResponse) => {
    this.createErrorMessage(error);
    this.router.navigate(['/404']);
  }
 
  private handleOtherError = (error: HttpErrorResponse) => {
    this.createErrorMessage(error);
    //TODO: this will be fixed later;
  }
 
  private createErrorMessage = (error: HttpErrorResponse) => {
    this.errorMessage = error.error ? error.error : error.statusText;
  }
}

First of all, we are injecting the Router which we use for redirecting the user to other pages in the code. In the method handleError(), we are checking for the error’s status code, and based on that we are calling the right private method for handling that error. The handle404Error() and handle500Error() functions are responsible for populating the errorMessage property. We are going to use this property as a modal error message or an error page message. We are going to deal with the handleOtherError() function later, thus the comment inside.

If you remember in the owner-list.component.ts file, we are fetching all the owners from the server. But there is no error handling in that file. So let’s continue with modifying that owner-list.component.ts file to implement the Angular error handling functionality:

import { Component, OnInit } from '@angular/core';
import { RepositoryService } from './../../shared/services/repository.service';
import { Owner } from './../../_interfaces/owner.model';
import { ErrorHandlerService } from './../../shared/services/error-handler.service';

@Component({
  selector: 'app-owner-list',
  templateUrl: './owner-list.component.html',
  styleUrls: ['./owner-list.component.css']
})
export class OwnerListComponent implements OnInit {
  public owners: Owner[];
  public errorMessage: string = '';

  constructor(private repository: RepositoryService, private errorHandler: ErrorHandlerService) { }

  ngOnInit() {
    this.getAllOwners();
  }

  public getAllOwners = () => {
    let apiAddress: string = "api/owner";
    this.repository.getData(apiAddress)
      .subscribe(res => {
        this.owners = res as Owner[];
      },
      (error) => {
        this.errorHandler.handleError(error);
        this.errorMessage = this.errorHandler.errorMessage;
      })
  }
}

That’s it.

You can try it out by changing the code in the server’s method GetAllOwners. As the first line of code add return NotFound() or return StatusCode(500, “Some message”), and you are going to be redirected to the right error page for sure.

Preparation for the Owner-Details Component

Let’s continue by creating the owner-details component:

ng g component owner/owner-details --skipTests

owner-details structure in Angular Error Handling

We need to modify the owner.module.ts file:

RouterModule.forChild([
      { path: 'list', component: OwnerListComponent },
      { path: 'details/:id', component: OwnerDetailsComponent }
    ])

As you can see, the new path has the parameter id. So when we click on the Details button we are going to pass this Id to our route and we are going to fetch the owner with that exact id in the OwnerDetails component.

We need to add a new interface to the _interfaces folder:

export interface Account{
    id: string;
    dateCreated: Date;
    accountType: string;
    ownerId?: string;
}

And modify the Owner interface:
import { Account } from './account.model';

export interface Owner{
    id: string;
    name: string;
    dateOfBirth: Date;
    address: string;

    accounts?: Account[];
}

By using a question mark, we are making our property as optional.

To continue, let’s change the owner-list.component.html:

<button type="button" id="details" class="btn btn-light" 
       (click)="getOwnerDetails(owner.id)">Details</button>

On a click event, we are calling the getOwnerDetails function and passing the owner’s id as a parameter. So we need to handle that click event in our owner-list.component.ts file.

Add an import statement:

import { Router } from '@angular/router';

Then modify constructor and add the function getOwnerDetails(id):
constructor(private repository: RepositoryService, private errorHandler: ErrorHandlerService, 
    private router: Router) { }

public getOwnerDetails = (id) => { 
  const detailsUrl: string = `/owner/details/${id}`; 
  this.router.navigate([detailsUrl]); 
}

Implementation of the Owner-Details Component

We have all the code for supporting the owner-details component. Now it is time to implement business logic inside that component.

Firstly, modify the owner-details.component.ts file:

import { Component, OnInit } from '@angular/core';
import { Owner } from './../../_interfaces/owner.model';
import { Router, ActivatedRoute } from '@angular/router';
import { RepositoryService } from './../../shared/services/repository.service';
import { ErrorHandlerService } from './../../shared/services/error-handler.service';

@Component({
  selector: 'app-owner-details',
  templateUrl: './owner-details.component.html',
  styleUrls: ['./owner-details.component.css']
})
export class OwnerDetailsComponent implements OnInit {
  public owner: Owner;
  public errorMessage: string = '';

  constructor(private repository: RepositoryService, private router: Router, 
              private activeRoute: ActivatedRoute, private errorHandler: ErrorHandlerService) { }

  ngOnInit() {
    this.getOwnerDetails()
  }

  getOwnerDetails = () => {
    let id: string = this.activeRoute.snapshot.params['id'];
    let apiUrl: string = `api/owner/${id}/account`;

    this.repository.getData(apiUrl)
    .subscribe(res => {
      this.owner = res as Owner;
    },
    (error) =>{
      this.errorHandler.handleError(error);
      this.errorMessage = this.errorHandler.errorMessage;
    })
  }

}

It is pretty much the same logic as in the owner-list.component.ts file, except now we have the ActivatedRoute imported because we have to get our id from the route.

After we execute the getOwnerDetails function, we are going to store the owner object with all related accounts inside the owner property.

All we have to do is to modify the owner-details.component.html file:

<div class="card card-body bg-light mb-2 mt-2">
  <div class="row">
    <div class="col-md-3">
      <strong>Owner name:</strong>
    </div>
    <div class="col-md-3">
      {{owner?.name}}
    </div>
  </div>
  <div class="row">
    <div class="col-md-3">
      <strong>Date of birth:</strong>
    </div>
    <div class="col-md-3">
      {{owner?.dateOfBirth | date: 'dd/MM/yyyy'}}
    </div>
  </div>
  <div class="row" *ngIf='owner?.accounts.length <= 2; else advancedUser'>
    <div class="col-md-3">
      <strong>Type of user:</strong>
    </div>
    <div class="col-md-3">
      <span class="text-success">Beginner user.</span>
    </div>
  </div>
  <ng-template #advancedUser>
    <div class="row">
      <div class="col-md-3">
        <strong>Type of user:</strong>
      </div>
      <div class="col-md-3">
        <span class="text-info">Advanced user.</span>
      </div>
    </div>
  </ng-template>
</div>

<div class="row">
  <div class="col-md-12">
    <div class="table-responsive">
      <table class="table table-striped">
        <thead>
          <tr>
            <th>Account type</th>
            <th>Date created</th>
          </tr>
        </thead>
        <tbody>
          <tr *ngFor="let account of owner?.accounts">
            <td>{{account?.accountType}}</td>
            <td>{{account?.dateCreated | date: 'dd/MM/yyyy'}}</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>

In this code example, we are conditionally displaying the owner entity. Moreover, we are displaying all the accounts related to this owner:

Beginner user - Angular Error Handling

 

owner-details 2 Angular Error Handling

Conclusion

By reading this post you have learned:

  • How to handle errors in separate service by using Angular Error Handling
  • The way to use the conditional rendering of the HTML page
  • How to create details page for your entity

Thank you for reading the post, hopefully, it was helpful to you.

In the next part of the series, I am going to show you how to create child components and how to use @Input, @Output, and EventEmmiters in Angular. By doing so, you are going to learn to split your components into smaller parts (parent-child relation).