Handle Angular 16 HTTP Requests with Observables

Last updated on: by Digamber
In this tutorial, we will show you how to handle asynchronous HTTP requests in Angular, to make the async requests, we will take the help of RxJS operators and Observable.

We are going to create a live country search module in an Angular app. For which we will be taking help of country list api and handle that API with RxJS observables and operators. Final output of this tutorial will look something like below.

Angular HTTP Requests with Observables

If you are an Angular developer or might face problems using the Observables in Angular, then this tutorial will surely help you to understand the easiest way of using Observables in an Angular to manage the HTTP response.

What Does Angular Say About Observables?

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

JavaScript Promises VS RxJS Observables

Let’s find out the difference between JavaScript Promises and RxJS Observables:

ObservablesPromises
Lazy in nature, require subscription to be invoked.Whereas Promise is excited in nature.
Supports multiple events (from 0 to many values).Supports single event.
It could either be synchronous or asynchronous.A Promise is always asynchronous.
It could either be synchronous or asynchronous.A Promise is always asynchronous.
Observables are cancelable.A Promise is not cancelable.

Check out the detailed explanation on how to use JavaScript Promises in Angular to manage HTTP response?

Install and Configure Angular Project

Let’s start by installing a basic Angular project for the managing Http request with the observables demo app.

ng new angular-http-observables

Get inside the project folder:

cd angular-http-observables

To create the live search module, we will be using Bootstrap’s UI components. Run below command to install bootstrap:

npm install bootstrap

Add the bootstrap.min.css path inside styles array inside the package.json file:

"styles": [
      "node_modules/bootstrap/dist/css/bootstrap.min.css",
      "src/styles.scss"
]

Create Live Search Layout with Bootstrap

Create a live search module for the Angular Http observable demo by pasting the following code inside app.component.html file:

<div class="container">
    <form>
        <div class="form-group search-bar">
            <!-- Search -->
            <input class="form-control" placeholder="Search..." type="text">
            <!-- Progres -->
            <div class="loader">
                <div class="c-three-dots-loader"></div>
            </div>
        </div>
        <!-- Search Result -->
        <div class="list-group">
            <div class="list-group-item list-group-item-action">
                <div _ngcontent-ert-c0="" class="media"><img alt="..." class="mr-3"
                        src="https://restcountries.eu/data/cod.svg">
                    <div class="media-body">
                        <p class="mt-0">Congo (Democratic Republic of the)</p>
                    </div>
                </div>
            </div>
            <div class="list-group-item list-group-item-action">
                <div class="media"><img alt="..." class="mr-3" src="https://restcountries.eu/data/fin.svg">
                    <div class="media-body">
                        <p class="mt-0">Finland</p>
                    </div>
                </div>
            </div>
            <div class="list-group-item list-group-item-action">
                <div class="media"><img alt="..." class="mr-3" src="https://restcountries.eu/data/nru.svg">
                    <div class="media-body">
                        <p class="mt-0">Nauru</p>
                    </div>
                </div>
            </div>
        </div>
    </form>
</div>

Import HttpClientModule

HttpClient is Angular’s tool for interacting with a web server over HTTP. Make HttpClient accessible in the entire Angular app in just two simple steps.

Firstly, import it inside the AppModule. And, secondly add HttpClient in the imports array :

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

Handle Angular HTTP Service with Observable

Next, generate the app/shared/country.ts class with the following command:

export class Country {
  public name!: string;
  public flag!: string;
}

Then, import CountryService and also insert the Angular service in the providers array in the AppModule. It make the service available in the entire app.

import { CountryService } from './shared/county.service';
@NgModule({
  declarations: [...],
  imports: [...],
  providers: [CountryService],
  bootstrap: [...]
})
export class AppModule { }

Next, generate the app/shared/search.service.ts using the following command:

ng generate service shared/county

Add the following code in app/shared/search.service.ts.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Country } from './country';
import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable({
  providedIn: 'root',
})
export class CountryService {
  endpoint: string = 'https://restcountries.com/v2/name/';
  constructor(private http: HttpClient) {}
  searchCountry(term: string): Observable<Country[]> {
    let url = `${this.endpoint}${term}`;
    if (!term.trim()) {
      return of([]);
    }
    return this.http
      .get<Country[]>(url)
      .pipe(catchError(this.handleError<Country[]>('countries', [])));
  }
  private handleError<T>(operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {
      console.log(`failed: ${error.message}`);
      return of(result as T);
    };
  }
}
  • In this Angular observable tutorial we starting with importing the observable from the rxjs library.
  • We used the rest countries API https://restcountries.eu/rest/v2/name/{name} to fetch the countries list.
  • Next, inject the HttpClient module in the constructor to make the HTTP GET request.
  • Then, we bind the Observable with search(term: string) method. It takes a string, basically entered by the user and will return an observable in which every item in the observable is Country[] list type.
  • To handle the error, we declared a handleError function, and we added error handling mechanism with the rxjs pipe operator.

Managing HTTP Response with Observable and RxJS Operators

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

OperatorDescription
SubjectA unique sort of RxJS Observable that supports a specific value to be multicasted to multiple Observers.
tapIt’s an RxJS pipeable operator which is used to perform side effect such as logging each value emitted by the source Observable
switchMapIt’s an RxJS operator it is widely used to get the latest value emitted by the observable.
debounceTimeThe debounceTime operator emits the latest value and helps in delaying the values transmitted by the root Observable for the specified time.
distinctUntilChangedReturns an observable series that carries only distinguished adjacent elements according to the key selector and the comparer.

Next, add the following code inside the app/app.component.ts:

import { Component, OnInit } from '@angular/core';
import { CountryService } from './shared/county.service';
import { Country } from './shared/country';
import { Observable, Subject } from 'rxjs';
import {
  tap,
  switchMap,
  debounceTime,
  distinctUntilChanged,
} from 'rxjs/operators';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit {
  loading: boolean = false;
  countries$!: Observable<Country[]>;
  private searchTerms = new Subject<string>();
  constructor(private countryService: CountryService) {}
  search(term: string) {
    this.searchTerms.next(term);
  }
  ngOnInit(): void {
    this.countries$ = this.searchTerms.pipe(
      tap((_) => (this.loading = true)),
      debounceTime(300),
      distinctUntilChanged(),
      switchMap((term: string) => this.countryService.searchCountry(term)),
      tap((_) => (this.loading = false))
    );
  }
}
  • Define the countries$ observable and mapped it with Observable;
  • Inject the CountryService inside the constructor.
  • Set RxJS new Subject() with serachTerms private variable. It will emit the latest value entered by the user incorporating with search(term: string) { } method in the live country search module.
  • Now, bind the countries$ variable with the searchTerms Subject along with RxJS pipeable operator. Inside this perform, the side effect with tap method here we are setting showing the loader, especially when the user enters any value.
  • Next, we are setting the delay for 300ms after that call the distinctUntilChanged() method. Next, take the latest value by using the swithcMap() operator and call the searchCountry method in it and passed the latest value in it.
  • When the request is called, and response is returned, then make the loader hidden by setting it up to false.

Display Data with Angular Async Pipe

Add the following code inside the app/app.component.html:

<div class="container">
  <form>
    <div class="form-group search-bar">
      <!-- Search -->
      <input
        type="text"
        class="form-control"
        placeholder="Search..."
        #searchBox
        (input)="search(searchBox.value)"
      />
      <!-- Progres -->
      <div class="loader" *ngIf="loading">
        <div class="c-three-dots-loader"></div>
      </div>
    </div>
    <!-- Search Result -->
    <div class="list-group">
      <div
        class="list-group-item list-group-item-action"
        *ngFor="let country of countries$ | async"
      >
        <div class="d-flex p-2 bd-highlight">
          <img src="{{ country.flag }}" alt="..." />
          <p class="ms-3">{{ country.name }}</p>
        </div>
      </div>
    </div>
  </form>
</div>

Lastly, we are going to display the data using the Async pipe. Let’s understand a little bit about the async pipe.

The async pipe subscribes to an Observable or Promise and gets the most recent value it has released. Async pipe signifies the component to be examined for the latest emitted value. The benefit of the Async pipe is that it unsubscribes the observable and provides memory leakage protection when the component is destroyed.

Finally, style the component by adding given css in styles.scss file.

body {
  background-color: rgba(0, 123, 255, 0.10980392156862745);
}
.container {
  max-width: 500px;
  margin-top: 50px;
}
.search-bar {
  position: relative;
}
.loader {
  position: absolute;
  top: 7px;
  right: 10px;
}
.form-control {
  border: none;
  padding: 0.775rem 0.75rem;
  height: auto;
}
img {
  max-width: 30px;
}
p {
  margin: 0;
  position: relative;
  top: -3px;
  font-size: 15px;
  font-weight: 500;
}
.list-group {
  max-height: 377px;
  overflow: hidden;
  overflow-y: auto;
  cursor: pointer;
  border-radius: 0 0 0.25rem 0.25rem;
}
.c-three-dots-loader {
  position: relative;
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  margin: -20px 20px 16px;
  animation-fill-mode: both;
  animation: three-dots-loader-animation 2s infinite ease-in-out;
  animation-delay: -0.16s;
  color: #323232;
}
.c-three-dots-loader:before,
.c-three-dots-loader:after {
  content: "";
  position: absolute;
  width: 12px;
  height: 12px;
  top: 0;
  animation: three-dots-loader-animation 2s infinite ease-in-out;
  border-radius: 50%;
}
.c-three-dots-loader:before {
  left: -16px;
  animation-delay: -0.32s;
}
.c-three-dots-loader:after {
  left: 16px;
}
@keyframes three-dots-loader-animation {
  0%,
  80%,
  100% {
    box-shadow: 0 20px 0 -24px;
  }
  40% {
    box-shadow: 0 20px 0 0;
  }
}

Conclusion

Finally, we have completed the Angular Observables tutorial with a live country search module example.

In this tutorial, we got started with a primary objective: Handling Angular HTTP response with Observables, we successfully achieved our goal, and i hope you learned a lot from this tutorial.

Digamber

A Full-stack developer with a passion to solve real world problems through functional programming.