Handle Angular 16 HTTP Requests with Observables
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.
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:
Observables | Promises |
---|---|
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?
Table of contents
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.
Operator | Description |
---|---|
Subject | A unique sort of RxJS Observable that supports a specific value to be multicasted to multiple Observers. |
tap | It’s an RxJS pipeable operator which is used to perform side effect such as logging each value emitted by the source Observable |
switchMap | It’s an RxJS operator it is widely used to get the latest value emitted by the observable. |
debounceTime | The debounceTime operator emits the latest value and helps in delaying the values transmitted by the root Observable for the specified time. |
distinctUntilChanged | Returns 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.