Table of Contents
Introduction
The async pipe can subscribe to a promise or an observable, returning the latest value that has been emitted. The unsubscription happens automatically after the component is destroyed for reducing the memory leak. It marks the component to be checked for the changes whenever a new value is emitted.
Here in this blog, we will see a manual search of the items listed using the async pipe, by using one of the Rxjs operators.
Service
First, to display the data we will get data from the HTTP call, creating the reusable service and injecting them into the constructor of the component. This makes the code more reusable, generic, and easier to refactor.
import { Injectable } from '@angular/core'; import { Country } from './data'; import { Observable } from 'rxjs'; import { HttpClient } from 'selenium-webdriver/http'; import { map } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class CountryService { constructor(private http: HttpClient) { } getCountryNames(): Observable<Country[]> { const requestUrl = "https://jsonplaceholder.typicode.com/posts"; return this.http.get<Country[]>(requestUrl) .pipe(map(response => response['data'])); } } The service is reusable and can be injected into the components of the application wherever needed as mentioned above.
App Component
Every time on coding the component.ts, make sure you have added all the import statements properly.
First, we will display the data from the service call with the subscribe method.
Without Observable
import { Component } from '@angular/core'; import { Country } from './data'; import { CountryService } from './country.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { listCountries: Country[] constructor(private service: CountryService) { this.getCountryNames(); } getCountryNames() { this.service.getCountryNames().subscribe(data=> { this.listCountries = data; }) } }
Here is where the data is stored in the variable listCountries, which can further be used in HTML with data interpolation.
HTML
<div *ngFor="let country of listCountries" > {{ i + 1 }}. {{ country.name | titlecase }} </div>
Using Observable
import { Component } from '@angular/core'; import { Observable } from 'rxjs'; import { Country } from './data'; import { CountryService } from './country.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { listCountries: Observable<Country[]> constructor(private service: CountryService) { this.listCountries = this.service.getCountries(); } }
Here is where the data from the service is stored into the Observable, which can further be accessed by using the async pipe in HTML.
HTML
<div *ngFor="let country of countries | async; let i = index" class="list-item col-12 col-md-6 col-lg-4"> {{ i + 1 }}. {{ country.name | titlecase }} </div>
Advantages of using async pipe with Observable
- Async pipe need not be manually unsubscribed in the component. Using ngOnDestroy angular takes care of subscriptions of async pipe for us.
- While using subscribe() we need to unsubscribe manually to avoid the memory leaks, at the end of the component life-cycle. Commonly we use takeUntil(unsubscribe$) to do this.
- Subscribing unwraps the property, can be used in multiple places in the component’s template without any other changes around them.
- For the unwrapped property, can be directly used without passing them to the template.
- For the performance to be greater, for the consistency, for OnPush change detection strategy, one-way data flow, and for automatic subscription management, we should be using async pipe as much as possible.
Dynamic search with Rxjs operator
For us to search the item in the list, we need a FormControl which can reflect the valueChanges observable that we can subscribe to. Changes in the input are published to the subscription.
HTML
<div class="pull-right searchbox"> <input class="form-control" [formControl]="searchText" placeholder="Search Skill"> </div>
Component.ts
this.listCountries = this.service.getCountries(); searchQuery$: Observable<any>; searchText: FormControl; constructor(private service: CountryService) { this.listCountries = this.service.getCountries(); this.searchText = new FormControl(''); this.searchQuery$ = this.searchText.valueChanges.startwith(''); }
Here, we have two observable streams:
- listCountries, which gives data from the database.
- searchQuery$, which is the input field to search data.
From this we will have to combine data to get a unified data stream that gives the resulting filtered data. So here comes the Rxjs combineLatest operator which takes two observables and creates a new observable that returns the latest value that is emitted.
Syntax
combineLatest(o1: Observable<T1>, o2: Observable<T2>): Observable<[T1, T2]>;
We need to transform the data stream so that it becomes an observable of filtered data. For this we are using the map operator which takes an observable and changes it into a customized stream of data.
this.searchQuery$.combineLatest(this.listCountries) .map(([query, searchList])
Further to filter only the data in the query, we will use array filter, and to make the search case-insensitive we will use .toLowercase()
this.searchResult= this.searchQuery$.combineLatest(this.listCountries) .map(([query, searchList]) => { return searchList.filter(state => state.name .toLowerCase().indexOf(query.toLowerCase()) !== -1) })
Finally, our code looks like this.
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { Country } from './data';
import { CountryService } from './country.service';
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
listCountries: Observable<Country[]>
searchResult: Observable<any>;
searchQuery$: Observable<any>;
searchText: FormControl;
constructor(private service: CountryService) {
this.listCountries = this.service.getCountries();
this.searchText = new FormControl('');
this.searchQuery$ = this.searchText.valueChanges.startwith('');
this.filterCountryList();
}
filterCountryList(){
this.searchResult= this.searchQuery$.combineLatest(this.listCountries)
.map(([query, searchList]) => {
return searchList.filter(state => state.name
.toLowerCase().indexOf(query.toLowerCase()) !== -1)
})
}
}
This gives you the easy way to filter the data of observables using the simple Rxjs operator.
Conclusion
We have used async pipe for the higher performance and used a simple dynamic search for the list of data from the observable using combineLatest operator.
Do you find it interesting? you might also like these articles. Top 10 Best Tech Companies For Employees To Work In The USA In 2020 and Top 10 IT Staffing and Recruiting Agencies in the USA.
If you have a business idea in your mind and in search 0f a reliable web development company, you are in the right place. Hire the best web developers in the industry from Agira technologies.