The article was originally published to my blog StackBlogger: Real World Examples of 5 Common Observable Operators
Reactive Extensions of JavaScript is a library for reactive programming. It is commonly known for its operators. The RxJS Operators are also known as Observable Operators.
What are Operators?
Operators are pure functions. They take observable as input and return output as observable.
5 Common Observable Operators
- map– a transformation operator works similar to Array.prototype.map
- switchMap– cancels the inner observable when outer observable emits a value
- catchError– catches the error and passes to error handling function
- debounceTime– discards values if emitted in less than specified time
- takeUntil– automatically unsubscribes from an observable
- distinctUntilChanged– emits only distinct values and duplicates ignored
Some real world uses of common observable operators
-
Typeahead search–
takeUnitl
withdebounceTime
operator can be used to achieve typeahead search -
Handle API errors–
catchError
can be used to handle error on a specific API call (or use global error handler to catch all the api errors at once) - Modify the response before binding on page using
map
operator (map
doesn’t modify the existing observable response, instead it returns a new observable)
Let’s understand each of them with examples.
Typeahead Search
Implementing a typeahead search with angular observable operators becomes really easy. It has switchMap
, debounceTime
, takeUntil
etc operators which are very useful in this scenario.
I will use debounceTime
and takeUntil
to achieve typeahead search. If you would like to know more about why this then visit a Case Study by Brecht Billiet here: Building a safe autocomplete operator
This example covers following observable operators- switchMap
debounceTime
, distinctUntilChanged
and takeUntil
In this example I will search the keyword in OMDB. It requires an API key to be passed in the url. If you do not have a key, get it from here.
Follow the link to get an OMDB API key.
app.service.ts
import { Injectable } from "@angular/core";
import { HttpClient } from '@angular/common/http';
import { Observable } from "rxjs";
@Injectable()
export class AppService {
constructor(private httpClient: HttpClient) {
}
movieSearch(q: string): Observable<any> {
return this.httpClient.get<any>(`https://www.omdbapi.com/?apikey=<your api key>&s=${q}`);
}
}
Replace <your api key>
with OMDB API key.
typeahead.component.ts
file code
import { Component, OnInit } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { debounceTime, switchMap, takeUntil, skip, distinctUntilChanged } from 'rxjs/operators';
import { AppService } from '../app.service';
@Component({
selector: 'app-typeahead',
templateUrl: './typeahead.component.html',
styleUrls: ['./typeahead.component.scss']
})
export class TypeaheadComponent implements OnInit {
term$ = new BehaviorSubject<string>('');
results$ = this.term$
.pipe(
debounceTime(1000),
distinctUntilChanged(),
switchMap(term =>
this.appService.movieSearch(term)
.pipe(
takeUntil(
this.term$.pipe(skip(1))
)
)
)
)
constructor(private appService: AppService) { }
ngOnInit(): void {
}
}
typeahead.component.html
file code
<div>
IMDB Movie Typeahead Search..
</div>
<br>
<input type="text" (input)="term$.next($any($event.target).value)">
Term- {{term$|async}}
<pre>{{results$|async|json}}
The Network calls look like this
Handle API Errors
An application should have a global error handler as well. But sometimes you need to show a custom message for a particular API. In that case this catchError comes as a handy operator.
This example covers following observable operators- catchError
Add the code in service file:
catchErrorExample() {
return this.httpClient.get('some invalid api link here').pipe(catchError(err => throwError('This is a custom Exception')));
}
I have used an invalid URL show that it can throw an error.
Call the catchErrorExample
method from component.
constructor(private appService: AppService) { }
ngOnInit(): void {
this.appService.catchErrorExample().subscribe(res => {
console.log('Response', res);
}, error => {
console.log('Error comes here', error);
});
}
You will notice the error in Browser Console.
Modify API Response
In many of the scenarios the use of map
operator comes in. One of them is modify your api response before binding on page.
This example covers following observable operators- map
Suppose you want to append the country code to every phone number based on the user country. Then this operator will help.
In this example I have used json-server
to create an users fake API.
db.json
file
{
"users": [
{
"id": 1,
"userName": "jameer",
"country": "India",
"phone": "1234567890",
"email": "jameer@email.com"
}
]
}
Start json server using below command:
json-server --watch db.json
Put getUser
function in app.service.ts
file
getUser(userId: number): Observable<UserModel> {
return this.httpClient.get<UserModel>(`http://localhost:3000/users/${userId}`);
}
app.component.ts
file
import { Component, OnInit } from '@angular/core';
import { AppService } from './app.service';
import { map } from 'rxjs/operators';
export interface UserModel {
id: number;
userName: string;
email: string;
country: string;
phone: string;
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
user: UserModel = {} as UserModel;
constructor(private appService: AppService) {
}
ngOnInit() {
this.getUsers();
}
getUsers() {
this.appService.getUser(1).pipe(
map((user: UserModel) => {
return {
...user,
phone: `+91 ${user.phone}`
}
})
).subscribe(user => {
this.user = user;
})
}
}
app.component.html
file
<div>
User Detail
</div>
<br>
<div>
<b>UserName: </b> {{user.userName}} <br>
<b>Email: </b> {{user.email}} <br>
<b>Country: </b> {{user.country}} <br>
<b>Phone: </b> {{user.phone}}
</div>
You will notice in the response above that it appends +91
at the beginning of Phone number. But initially it was not there in the db.json
file. That is done using map
operator.
What’s next
This article covers real world uses of some common Angular Observable Operators. I hope it could make a little difference in your understanding about RxJS Operators. That’s all for now.
If you are looking to optimize your Angular Project then I would recommend checking out these articles:
5 Best Ways To Optimize Angular For Scaling (2021)
7 Best Ways To Improve Angular Code (2021)
Top comments (0)