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.

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

To download the source code for this article, you can visit our GitHub repository.

Let’s start.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

Creating the 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 --skip-tests

Then, 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 {
  errorMessage: string = "500 SERVER ERROR, CONTACT ADMINISTRATOR!!!!";
  
  constructor() { }

  ngOnInit(): void {
  }

}

Then, let’s modify the internal-server.component.html file:

<p> {{errorMessage}} </p>

Additionally, we are going to modify the internal-server.component.css file:

p{
  font-weight: bold;
  font-size: 50px;
  text-align: center;
  color: #c72d2d;
}

Finally, let’s modify the routes array in the app-routing.module.ts file:

const routes: Routes = [
  { 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 shared/services folder, we are going to create a new service and name it error-handler:

ng g service shared/services/error-handler --skip-tests

Next, let’s modify that error-handler.service.ts file:

import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
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 inject the Router, which we use to redirect the user to other pages from the code. In the handleError() function, we check for the error’s status code and based on that we call the right private method to handle 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 on, thus the comment inside.

If you remember 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 by modifying that owner-list.component.ts file to implement the Angular error handling functionality:

import { Component, OnInit } from '@angular/core';

import { Owner } from './../../_interfaces/owner.model';
import { OwnerRepositoryService } from './../../shared/services/owner-repository.service';
import { ErrorHandlerService } from './../../shared/services/error-handler.service';
import { HttpErrorResponse } from '@angular/common/http';

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

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

  ngOnInit(): void {
    this.getAllOwners();
  }

  private getAllOwners = () => {
    const apiAddress: string = 'api/owner';
    this.repository.getOwners(apiAddress)
    .subscribe({
      next: (own: Owner[]) => this.owners = own,
      error: (err: HttpErrorResponse) => {
          this.errorHandler.handleError(err);
          this.errorMessage = this.errorHandler.errorMessage;
      }
    })
  }

}

That’s it. We have to pay attention that now, we are passing a JSON object inside the subscribe function. The next property will trigger if the response is successful, and the error property will handle the error response.

We can try it out by changing the code in the server’s GetAllOwners method. Just as a first code line, we can add return NotFound() or return StatusCode(500, “Some message”), and we 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 --skip-tests

To enable routing to this component, we need to modify the owner-routing.module.ts file:

const routes: Routes = [
  { path: 'list', component: OwnerListComponent },
  { path: 'details/:id', component: OwnerDetailsComponent }
];

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

But, in order to be able to do that, 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 optional.

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

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

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

First, let’s add an import statement:

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

Then, we are going to modify the constructor to add the router:

constructor(private repository: OwnerRepositoryService, private errorHandler: ErrorHandlerService,
  private router: Router) { }

And add the getOwnerDetails(id) function:

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

We create a URI for our details component with the id parameter and then call the navigate function to navigate to that component.

Finally, let’s just add one more function to fetch a single owner inside the owner-repository.service.ts file:

public getOwner = (route: string) => {
  return this.http.get<Owner>(this.createCompleteRoute(route, this.envUrl.urlAddress));
}

Implementation of the Owner-Details Component

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

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

import { HttpErrorResponse } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { Owner } from './../../_interfaces/owner.model';
import { Router, ActivatedRoute } from '@angular/router';
import { OwnerRepositoryService } from './../../shared/services/owner-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 {
  owner: Owner;
  errorMessage: string = '';

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

  ngOnInit() {
    this.getOwnerDetails()
  }

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

    this.repository.getOwner(apiUrl)
    .subscribe({
      next: (own: Owner) => this.owner = own,
      error: (err: HttpErrorResponse) => {
        this.errorHandler.handleError(err);
        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>

Here, we display the owner entity with all the required data. Also, if the owner has more than two accounts we conditionally show a different template (#advancedUser) for that field. Finally, we display all the accounts related to this owner:

Beginner user - Angular Error Handling

We can also take a look at the advanced user:

Advanced user - Angular Error Handling

Conclusion

By reading this post we’ve learned how to handle errors in a separate service by using Angular Error Handling. Also, we used conditional HTML rendering to show different data on the page with all the required details.

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, we are going to learn to split our components into smaller parts (parent-child relation).

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!