Click onto create credentials for your app.After obtaining the API key, navigate back to your project in the Angular IDE and create a TypeScriptfile in the app folder.You can name the file, and this will be its contents:

1 const API_KEY = 'your-api-key-here' ; export default API_KEY ;

app.component.ts

1 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.

src / app / app . component . ts

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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 ) ) ; } }

After this, you will import the API key into thefile which is where we will write the code to query the YouTube API. Do it like this:Next, let’s build our form in thefile. Add the following code:

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:

1 2 3 4 5 6 7 8 9 10 11 12 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:

1 2 3 4 5 6 7 < 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

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.







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.







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. Let’s run the Angular app through the server view in Angular IDE. In order to open it, select, then, then. Right click onand click. Angular IDE serves the application on localhost port 4200 by default, so open up in your browser to see the running app.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. 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).



1 2 3 4 5 6 7 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.







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. First, let’s move the whole search function into a separate method. Let’s call it. 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.

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. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 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



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.



1 import { switchMap } from 'rxjs/operators' ; 1 2 3 4 5 6 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 ) ) ; } will cancel out the prior event and just use the value in the latest event. With, 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, whilewill 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).and about the different types of O bservable 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. 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.

1 import { switchMap , debounceTime } from 'rxjs/operators' ; 1 2 3 4 5 6 7 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. filter distinctUntilChanged distinctUntilChanged only emits when the current value is different from the last. We can also add other operators likeand. Filter only emits those items from an Observable that pass a predicate test, whileonly 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:



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 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 ) ) ; } } 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: 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:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 <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 >



Your result should now look like this: 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.

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.