In this tutorial, we will learn how to handle an asynchronous Angular HTTP request using observable.

We are going to build a live book search app with Google Book API.

This app will help us to understand the easiest way of using observable in Angular projects to manage the Http response.

Before proceeding further with the tutorial we should have per-knowledge of the following:

  1. Basic knowledge of JavaScript
  2. A little knowledge of the framework (Angular)
  3. How to make an API call
  4. How to use Google book Api.

But for the Google book API, we will still cover the basics of it. So if you have not used it before we still got you covered

We should have:

  1. node.js install in our system
  2. Angular cli also installed in our system
  3. A text editor

For the text editor, you can use anyone that you are familiar with but I will be using Visual Studio Code for this project. 

Let’s look out the outline of what we will cover while building the project.

Outline

  1. What is Observable
  2. What is Http
  3. Prerequisite
  4. Creating a live book search
  5. Import HttpClientModule
  6. Handle Angular HTTP Service with Observable
  7. Managing HTTP Response with Observable
  8. Display Data with Angular
  9. Conclusion

What is Observable

Before we start by explaining what is observable let see what Angular says about observable.

”Observables provide support for passing messages between publishers and subscribers in your application. Observables offer significant benefits over other techniques for event handling, asynchronous programming, and handling multiple values.— from Angular.io

Knowing what angular tells about observable, Now what is observable? 

Observable services are patterns that allow you to effectively deal with data — allowing you to parse, modify and maintain data in an event-based system.

it is an ES7 feature which means you need to make use of an external library to use it today.

RxJS is a good one. RxJS also provides Observable operators that you can use to manipulate the data being emitted. Some of these operators are:

  • Map
  • Filter
  • Take
  • Skip
  • Debounce
  • Skip
  • Debounce

Difference between JavaScript Promises and RxJS Observables:

Observable Promises
Observables are cancelable. A Promise is not cancelable.
It could either be synchronous or asynchronous. A Promise is always asynchronous.

What is Http

The $http service is used to send or receive data from the remote server using browser’s XMLHttpRequest or JSONP.$http is a service as an object. It includes following shortcut methods:

Method Description 
$http.put() Perform Http PUT request. 
$http.post() Perform Http POST request. 
$http.patch() Perform Http PATCH request.
$http.jsonp() Perform Http JSONP request. 
$http.head() Perform Http HEAD request. 
$http.get() Perform Http GET request. 
$http.delete() Perform Http DELETE request. 

Knowing what observable and http are. We could also move further to explain it and see how it works in a practical scene. 

Prerequisite

Let’s start by creating a basic Angular project.

ng new BookApp
# Would you like to add Angular routing?
# Select n and Hit Enter. 

Get inside the project folder:

cd BookApp

We will be using Bootstrap. Run below command to install bootstrap:

npm install bootstrap --save

After the creating and installation of the above setup for the project, we can now proceed with writing our code.

In our project folder open app.component.html and write the code below:

<div *ngif="!loading" class="container-fluid hero-page1">
  <div class="container">
    <div class="row justify-content-center align-items-center">
      <div class="col-lg-12 col-md-12 col-sm-12">
        <h1>Search to know more about your favourite books</h1>
        <input [formcontrol]="queryField" id="keyword" type="search" class="form-control"  aria-describedby="emailHelp">
      </div>
    </div>
  </div>
</div>

In the above code, we created a simple text input for search and HTML tags that will host the search result suggestions and some tags that will display our request and also we bind formControl to our input tag to get the value and send it to the server. If we now serve our app you will see our default input. To serve our app run the command below:

ng serve -o

Import HttpClientModule

Before we move further to trigger our input tag and calling the Google Book API, we need to have our httpClientModule imported so we can be able to interact with the Google Book API through http.

import { HttpClientModule } from "@angular/common/http";
@NgModule({
  declarations: [...],
  imports: [
    HttpClientModule
  ],
  providers: [...],
  bootstrap: [...]
})
export class AppModule { }

Handle Angular HTTP Service with Observable

Let generate an ApiService for this purpose. Run the below command:

 ng generate service api 

We will wonder what the service is. Let look at what a service is in angular.

Service is a broad category encompassing any value, function, or feature that an app needs. A service is typically a class with a narrow, well-defined purpose. 

We need a service to enable us to make our API call and for us to use the response in any of the components. We named it ApiService because it is where we are going to make our API call to the Google Book Api and handling our error responses.

You can name yours whatever you like but the main purpose is knowing what it does. Let import our ApiService in our app.module to make it available in the entire app.

import { ApiService } from './api.service'
@NgModule({
  declarations: [...]
  imports: [...],
  providers: [ApiService],
  bootstrap: [...]
})
export class AppModule { }

After importing it in the module, we can now go ahead to make our fetch Api call. Open the app.service.ts add the below code.

import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
@Injectable({
  providedIn: "root"
})
export class ApiService {
  key = "your_key";
  constructor(private httpClient: HttpClient) {}
  getBook(queryField: string) {
    return this.httpClient.get(
      `https://www.googleapis.com/books/v1/volumes?q=${queryField}&maxResults=39&keyes&key=${this.key}`
    );
  }
}
  • In the service component, we started by importing the HttpClientModule to enable us make a request to serve through http.
  • We declared a variable key and assigned our Google Book API key value to it.
  • We created a function called getBook() and called http get() request to Google Book API endpoint.

I know that some of us have not to use the Google Book Api and we are just starting out. Below is a basic explanation of what Google Book API is.

Google Book API is intended for developers who want to write applications that can interact with the Books API. Google Books has a mission to digitize the world’s book content and make it more discoverable on the Web. The Books API is a way to search and access that content, as well as to create and view personalization around that content.

Using the API

The Google Books API Endpoint is: https://www.googleapis.com/books/v1/volumes?

The API has a lot of parameters that we can use, but we’ll only need 3 of them which are :

q: the search query tapped by the user in the search input.

maxResults: The maximum number of results to return. The default is 10.

client_id: the client ID generated in your Google Books Console account.

In order to use the Google Books API, you have to create a developer account and register your app and also to generate an API-KEY.

To know more about the book API you can use the above link.

Managing HTTP Response with Observable

To handle the HTTP response via observable we will be using the following RxJS operators.

OperatorDescription
distinctUntilChangReturns an observable series that carries only distinguished adjacent elements according to the key selector and the comparer.
debounceTimeThe debounceTime operator emits the latest value and helps in delaying the values transmitted by the root Observable for the specified time.

First, we will start by importing the FormControl and FormBuilder. We converted our input to use FormControl which expose a valueChange Observable to always get the value whenever it is changed and to send the value to the serve.app.component.ts

import { FormControl, FormBuilder } from "@angular/forms";
@Component({
  selector: "app-view",
  templateUrl: "./view.component.html",
  styleUrls: ["./view.component.scss"]
})
export class ViewComponent implements OnInit {
  loading;
  queryField: FormControl = new FormControl();
  constructor(
    private formBuilder: FormBuilder,
  ) {}
  ngOnInit() {
  }
}

Next is to inject our ApiService so that we can send a value to the Api endpoint and to get a response back from the serve.app.component.ts.

import { ApiService } from "../api.service";
@Component({
  selector: "app-view",
  templateUrl: "./view.component.html",
  styleUrls: ["./view.component.scss"]
})
export class ViewComponent implements OnInit {
 constructor(
    private apiService: ApiService
  ) {}
ngOnInit() {
  }
}

Lastly, we are now going to trigger our input to get the value and send it to the server. In the ngOnInit() add the below code.app.component.ts

ngOnInit(){
    this.queryField.valueChanges.subscribe((queryField: any) => {
        let space = queryField.replace(/\s/g, "");
        if (space.length > 2) {
          this.apiService.getBook(queryField).subscribe((result: any) => {
            this.loading = true;
            setTimeout(() => {
              this.items = result.items;
            }, 2000);
          });
        }
      });
}

What the above code does is:

  • The this.queryField holds the value when a user is typing.
  • The .valueChanges is a property of Abstract-control that emits an event every time when the value of control changes.
  • The variable space we declared stop the .valueChanges from sending a space as a word to the serve.
  • The next thing we did is subscribing to the Google Book Api by calling the getBook() function we created in the ApiService and passing it the value we get from our input as a parameter.

If we observe while making our search, we notice each letter we type fire’s a request to the server and we don’t want it to fire a request immediately we type. We want it to delay a little before sending a request because we may be typing a long word. To handle this, we need to import debounceTime and distinctUntilChanged

import { debounceTime, distinctUntilChanged, switchMap } from "rxjs/operators";

The debounceTime operator emits the latest value and helps in delaying the values transmitted by the root Observable for the specified time.

The distinctUntilChanged Returns an observable series that carries only distinguished adjacent elements according to the key selector and the compare.

The full code of the above explanation:app.component.ts

import { FormControl, FormBuilder } from "@angular/forms";
import { ApiService } from "../api.service";
import { debounceTime, distinctUntilChanged, switchMap } from "rxjs/operators";
@Component({
  selector: "app-view",
  templateUrl: "./view.component.html",
  styleUrls: ["./view.component.scss"]
})
export class ViewComponent implements OnInit {
  loading;
  queryField: FormControl = new FormControl();
  constructor(
    private formBuilder: FormBuilder,
    private apiService: ApiService
  ) {}
  ngOnInit() {
  this.loading = false;
    this.queryField.valueChanges
      .pipe(debounceTime(1000), distinctUntilChanged())
      .subscribe((queryField: any) => {
        let te = queryField.replace(/\s/g, "");
        if (te.length > 2) {
          this.apiService.get(queryField).subscribe((result: any) => {
            this.loading = true;
            setTimeout(() => {
              this.items = result.items;
            }, 2000);
          });
        }
      });
  }
}

Display Data with Angular

Lastly, we are going to display the data using the angular data binding to view the response and also we are going to use ngIf and ngFor to loop through the response. Add the following code inside the app/app.component.html:

<div *ngIf="loading" class="container-fluid mt-4">
      <div *ngIf="items" class="col-12 mt-5">
        <div class="row justify-content-around">
          <div
            *ngFor="let product of items"
            class="col-lg-3 col-md-6 col-sm-12 book-display"
          >
            <div class="image">
              <img
                *ngIf="product.volumeInfo.imageLinks"
                src="{{%20product.volumeInfo.imageLinks.thumbnail%20}}"
                alt=""
              />
            </div>
            <div *ngFor="let aut of product.volumeInfo.authors">
              <span> Authors: {{ aut }}</span>
            </div>
            <div class="details">
              <span>Title: {{ product.volumeInfo.title }}</span> <br />
              <br />
                  <a [routerLink]="['/new', combineSlug(product.id)]">Detai</a>
              <a (click)="goToLink(product.volumeInfo.previewLink)">preview</a>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

Conclusion

In this tutorial, we started by knowing what Observable and Angular Http service is, How to handle Angular HTTP response with Observable and http while building our live book search app, and also how to use Google Book Api. I hope this tutorial was helpful.