DEV Community

Jameer Khan
Jameer Khan

Posted on • Edited on • Originally published at stackblogger.com

Real World Examples of 5 Common Observable Operators

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 searchtakeUnitl with debounceTime operator can be used to achieve typeahead search
  • Handle API errorscatchError 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.

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}`);
    }
}
Enter fullscreen mode Exit fullscreen mode

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 {
  }
}
Enter fullscreen mode Exit fullscreen mode

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}}
Enter fullscreen mode Exit fullscreen mode

The Network calls look like this
Typeahead Search Network Calls

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')));
}
Enter fullscreen mode Exit fullscreen mode

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);
    });
  }
Enter fullscreen mode Exit fullscreen mode

You will notice the error in Browser Console.
Typeahead Search Network Calls

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"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

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}`);
 }
Enter fullscreen mode Exit fullscreen mode

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;
    })
  }
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

The output is:
Typeahead Search Network Calls

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)