facebook
George Anderson
Frontend and backend development guru, lover of all things computer... and cats! Hopes to make your coder life a breeze by sharing some tips and tricks of the trade.
Posted on Feb 21st 2018

Angular, as we all know, is a JavaScript framework that makes it easy to build web applications. RxJS is really about bringing the principles of functional programming to JavaScript.

Functional Programming in JavaScript?

It all started with the rise of asynchronicity or asynchronous JavaScript,where we have data that is flowing through our application coming from different sources, for example, API, Web sockets and User-triggered events and timers. We have a situation where we can’t control when any of this data is sent through our application. Neither can we decide the order in which it happens, so having a more functional paradigm would help resolve that. You want your application to be more predictable, but the scaling up of applications that run client side means less predictability.

So then comes the rise of Promises, which are supposed to solve problems we’ve all had with XHRs, which came to save us from the callback hell. You will discover that the more you use Promises with their then and catch functions, the more they look like callbacks again.

Few Problems with Promises 

  • Promise is defined where the data is created, not where it is being consumed. 
  • As your application gets bigger, Promises become hard to manage. 
  • What if I want to retry a failed call? Now we are back to callback hell again.

Then Observables Arrived

RxJS is all about unifying the ideas of Promises, callbacks and data flow, and making them easier to work with.
An Observable is an array or a sequence of events over time. It has at least two participants. The creator (the data source) and the subscriber (subscription – where data is being consumed).

For example, let data = http.get(‘/api.json’) . In Angular, data is going to be an Observable of responses, because the HTTP.get method returns a Promise.

Ready to modernize your JavaScript experience? Try JSjet with its syntax highlighting, call and type hierarchies, intelligent content assist, source refactoring and formatting, and much more! Make sure your code works – use instant validation and integrated debugging.

Search YouTube API Using Angular with Observable

To demonstrate the power of an Observable with Angular, we will create a sample project, querying the YouTube API. 

Create a New Angular App

Open Angular IDE, click `File` in the top menu, then select `New`, then click `Angular Project`.
creating a project in Angular IDE
This will take us to a page where we can specify the project details, such as name, version of the Angular-CLI and Node to use. Now enter `youtube-searcher` as the project name, 1.6.8 as the Angular CLI version, 9.5.0 as the Node.js version, and 5.6.0 as the npm version. Click Next,then click Finish.

angular Ide project

Get an API Key from Google Console

To interact with the YouTube API, we need to get an API Key from the Google Console, so navigate here and follow the steps to obtain your API Key. You will have to create an app to obtain API credentials for it. This will enable our app to submit API requests to the YouTube API. 

youtube api key

Click on `Create credentials` to create credentials for your app. 

youtube key

After obtaining  the API key, navigate back to your project in the Angular IDE and create a TypeScript `src` file in the app folder.

typescript source file

You can name the file `api-key.ts`, and this will be its contents:
const API_KEY = 'your-api-key-here'; export default API_KEY;
After this, you will import the API key into the `app.component.ts` file which is where we will write the code to query the YouTube API. Do it like this:
import API_KEY from './api-key';


Query YouTube API

The YouTube API doesn’t return a direct list of results, but rather a group of meta data where it has one property called Items that contains the list of results. 

Next, let’s build our form in the src/app/app.component.ts file.  Add the following code:
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs/Observable';
import API_KEY from './api-key';

const API_URL = 'https://www.googleapis.com/youtube/v3/search';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'Angular RxJs YouTube Searcher';
  searchForm: FormGroup;
  results: any;

  constructor(private formBuilder: FormBuilder, private http: HttpClient) {
    this.searchForm = this.formBuilder.group({
      search: ['', Validators.required]
    });

    this.searchForm.controls.search.valueChanges.subscribe(result => console.log(result));
  }
} 

The Search Input Form is using the Angular Reactive Forms API. Learn more about reactive forms here.

Because the `valueChanges` method returns an Observable, here in our example it returns an Observable of characters typed in the input element. The subscribe function call subscribes to each value, saves them in the result variable and displays that in the browser console with console.log.

We are also using the arrow function expression=> ) on the subscribe call, which has a shorter syntax than the function expression. It’s very efficient when you want to write short functions like the type you would use in a subscribe method. Read more about arrow function here. 

Using the Angular Reactive Form API

To start using the Reactive Forms API and HTTP Client, we need to import the modules into the `app.module.ts` file, so update the module with the following imports and add the modules to the imports array:

    import { ReactiveFormsModule } from '@angular/forms';
    import { HttpClientModule } from '@angular/common/http';
    
    .....
    
    imports: [
        BrowserModule,
        ReactiveFormsModule,
        HttpClientModule
    ],
    
    ......

And in the template file, src/app/app.component.html, add the following code:

<h3>{{ title }}</h3>

<form [formGroup]="searchForm">
<label for="search">Search YouTube</label>
<br />
<input formControlName="search" id="search" />
</form>

Also note that the `searchForm` and search both correspond to the names given in the template, because that’s how Angular knows how to connect the template html code to the typescript component code.

 Running the Angular App

Let’s run the Angular app through the server view in Angular IDE. In order to open it, select `Window`, then `Show view`, then `Servers`. Right click on `youtube-searcher` and click `Start Server`. Angular IDE serves the application on localhost port 4200 by default, so open up in your browser to see the running app.

run from server tab

We won’t be doing anything with our search for now, we just need to verify that the form is working. To open up the Console, right click anywhere on the page of your browser, click Inspect, and select the Console tab.

console

As you can see, everything that is typed into the Input form gets logged out – that’s the first example showing how Angular leverages the power of the Observable method.

Next, we need to pass that value now and use it to query the YouTube API for results. Let’s do that next. Update the search section in file to look like this (we will console.log the value that we get from the YouTube API).

                             this.searchForm.controls.search.valueChanges.subscribe(searchTerm => {
      this.http.get<any>(
         `${API_URL}?q=${searchTerm}&key=${API_KEY}&part=snippet`)
          .subscribe(result => {
        console.log(result);
   });
});

The result should look like this when you now try to search. As you can see, we are getting back the responses from the API, and if you explore well, you will see the Items object in the response.

console 3

Improve and Refactor the Code

However, we now have some issues. First, you will notice that currently we query the API every time we type in a word into the input form. This is not what we would want, else we would quickly exceed our usage and query limit. Therefore, we will use some operators from the RxJS library to clean up the code and achieve the desired result.

First, let’s move the whole search function into a separate method. Let’s call it `search`. Then we will call it inside ngOnInit instead of in the constructor, as we are currently doing. In a larger Angular app, it is a bad practice to call functions, especially Angular functions inside the constructor. You don’t want to call any of your Angular functions inside the constructor because Angular does not have any control over when the constructor gets called or initiated on page load. You can read more here on the difference between ngOnInit and the constructor.

After refactoring the code, the result looks like this. Notice the importation of `OnInit` from`@angular/core` on line 1 and its implementation on line 20. After this update, your app’s behavior should remain the same. Go ahead and give it a try.
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs/Observable';
import API_KEY from './api-key';

const API_URL = 'https://www.googleapis.com/youtube/v3/search';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'Angular RxJs YouTube Searcher';
  searchForm: FormGroup;
  results: any;

  constructor(private formBuilder: FormBuilder, private http: HttpClient) {
    this.searchForm = this.formBuilder.group({
      search: ['', Validators.required]
    });
  }

  ngOnInit() {
    this.search();
  }

  search() {
     this.searchForm.controls.search.valueChanges.subscribe(searchTerm => {
      this.http.get<any>(
         `${API_URL}?q=${searchTerm}&key=${API_KEY}&part=snippet`)
         .subscribe(result => {
        console.log(result);
      });
    });
  }
}

So back to getting the desired result, our first issue is with the nested subscription, which is common when using Promises. This sort of nesting is called the pyramid of doom, which is one of the major reasons we don’t want to use *a* Promise in the first place. *We* want to avoid that at all *costs*, and to do that, Observable provides an operator called Pipeable Operator, which allows you to *do this* nesting of operations in a more efficient way.

Thus, we will refactor our code to leverage the pipe. The first operator we will use is the `switchMap` operator.

Understanding Operators in RxJS

`switchMap` will cancel out the prior event and just use the value in the latest event. With `switchMap`, every time a new Observable is returned, it unsubscribes from the previous one and subscribes to the latest one. Basically, its switching from one Observable to the latest Observable, while `mergeMap` will let all the Observables go (it won’t cancel the old ones – this is where you could get the race conditions, which results in a weird behavior where the result of the first event could be returned instead of the desired one). Read more about race conditions and about the different types of Observable maps.

Now, with the application of switchMap, should you test this out, you will notice that the request is not sent to the API upon every keystroke anymore.

import { switchMap } from 'rxjs/operators';
search() {
     this.searchForm.controls.search.valueChanges.pipe(
      switchMap(searchTerm => this.http.get<any>(
        `${API_URL}?q=${searchTerm}&key=${API_KEY}&part=snippet`))
     ).subscribe(result => console.log(result));
}

But something still seems off. If you are on a fast network, you will notice that at least two objects are still being returned, which means the subscription didn’t wait for you to finish typing your query before the request was sent. We can delay the sending of request a little bit by adding another operator called debounceTime, a really helpful operator that will debounce the events. It will discard emitted values that takes less than the specified time between outputs.

import { switchMap, debounceTime } from 'rxjs/operators';
search() {
     this.searchForm.controls.search.valueChanges.pipe(
      debounceTime(500),
      switchMap(searchTerm => this.http.get<any>(
        `${API_URL}?q=${searchTerm}&key=${API_KEY}&part=snippet`))
     ).subscribe(result => console.log(result));
}
So `switchMap` cancelled prior request but only at the browser level. Once those network requests go out, they are still hitting the server, which means an unwanted request might still be sent. Now debounce will make the request wait 500 milliseconds until the events are fired(until requests are sent to the server) – events will not be sent to the server until you have finished typing in the request. This means only one API call/request will go to the server.
We can also add other operators like filter and distinctUntilChanged. Filter only emits those items from an Observable that pass a predicate test, while `distinctUntilChanged` only emits when the current value is different from the last.

We also have to apply a map operator to map through the values that are returned from the API, because the request returns a big object with different metadata, and the only object we need from this is the Items object.

The final code looks like this:

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs/Observable';
import { map, switchMap, debounceTime, distinctUntilChanged, filter } from 'rxjs/operators';
import API_KEY from './api-key';

const API_URL = 'https://www.googleapis.com/youtube/v3/search';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'Angular RxJs YouTube Searcher';
  searchForm: FormGroup;
  results: Observable<any>;

  constructor(private formBuilder: FormBuilder, private http: HttpClient) {
    this.searchForm = this.formBuilder.group({
      search: ['', Validators.required]
    });
  }

  ngOnInit() {
    this.search();
  }

  search() {
     this.results = this.searchForm.controls.search.valueChanges.pipe(
       debounceTime(500),
       filter(value => value.length > 3),
       distinctUntilChanged(),
       switchMap(searchTerm => this.http.get<any>(`${API_URL}?q=${searchTerm}&key=${API_KEY}&part=snippet`)),
       map(response => response.items)
     );
  }
}

Angular’s HTTP method returns an Observable instead of returning a Promise. This Observable then needs to be subscribed to for it to be consumed. Using the pipeable operator on the subscription, we are able to transform the returned subscription, and now we need to use async pipe to consume the subscription on the template side.

To make Angular and RxJS better together, Angular has created an async pipe, used in the template.

While it’s possible to do a standard subscription on the component side, it can also be done in the declarative template style, which is generally recommended by Angular. Async will automatically subscribe to the Observable for you, and it will automatically unsubscribe for you as well when you navigate out of the page or component.

And in the HTML template we have this:


<h3>{{ title }}</h3>

<form [formGroup]="searchForm">
<label for="search">Search YouTube</label>
<br />
<input formControlName="search" id="search" />
<div *ngFor="let result of results | async">
<a [href]="'https://www.youtube.com/watch?v=' + result.id.videoId" target="_blank">
{{result.snippet.title}}
</a>
<p>{{result.snippet.description}}</p>
<img [src]="result.snippet.thumbnails.default.url" style="width: 100%; max-width: 250px;" />
</div>
</form>
What the async pipe does depends on whether you give it a Promise or an Observable. It creates and unwraps the subscription or Promise, and displays the data when the component is loaded, when the template is running, and then automatically unloads and unsubscribes when the component is unloadedfor example when you navigate to another page with a new component). This manages the whole life cycle of subscription to Observables, so you don’t have to be managing any of that yourself.

Your result should now look like this:

final output

Points to take note of:
  • With a Promise you can only handle one event
  • With an Observable you can handle multiple events
  • .subscribe() is similar to .then()
  • An Observable can do everything that a Promise can do, plus more
  • Use Angular’s HttpClient to handle API calls.
  • The Angular framework uses a lot of RxJS
  • Many Angular APIs use Observables – we can take advantage of this to build some really cool stuff

Conclusion

RxJS is a really important part of your Angular toolbox – even though it doesn’t solve every problem, it will definitely make you a better Angular developer. Asynrhonous experiences are the norm for today’s websites. With the combined power of Angular and RxJS, you’re well on your way to delivering this experience to your users too.