In this tutorial, we’ll learn how to integrate a YouTube API in Angular.
Introduction :
The YouTube Data API lets you incorporate functions normally executed on the YouTube website into your own website or application. We can retrieve a list of different types of resources from the API. A resource represents a type of item that comprises part of the YouTube experience, such as a video, a playlist, or a subscription.
First you need to set YouTube API key to integrate it in the project.
Set up the YouTube API
To get API key follow the below steps:
- Go to the Google developer console to create a new project.
- Here when you create new project it will show you in below list,
- Select a project.
- In the left sidebar, select library & enable YouTube Data API v3.
- Navigate to the credentials page by clicking on Credential located on the sidebar menu.
- Click on the
+ CREATE CREDENTIALS
button located at the top of the page and selectAPI key.
A new API key should be created.
Create New Project
Create a new project from terminal in VS code by using following command.
ng new YouTubeAPI
here we need to create component for search box and container.
Create a new module called shared by running the following command in the terminal.
ng g module shared
now let’s create a service in this module by using following command in terminal.
ng g service shared/services/search
write the code in search.service.ts file as below:
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { map } from 'rxjs/operators'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class SearchService { private API_URL = 'https://www.googleapis.com/youtube/v3/search'; private API_KEY = 'YOUR API KEY'; constructor(private http: HttpClient) {} getVideos(query: string): Observable <any> { const url = `${this.API_URL}?q=${query}&key=${this.API_KEY}&part=snippet&type=video&maxResults=10`; return this.http.get(url) .pipe( map((response: any) => response.items) ); } }
The getVideos method receives a search query string passed in from the input component, which we have to create. It then uses the http
get method to send off a request to the URL constructed. It returns a response that we handle with the map operator. The list of YouTube video details is stored in the response.item object.
Now create interface. Run following command
ng g interface video
Add the following code in interface
export interface Video { videoId: string; videoUrl: string; channelId: string; channelUrl: string; channelTitle: string; title: string; publishedAt: Date; description: string; thumbnail: string; }
We will be using Semantic-UI to provide styling to our application.
Add the following line in index.html file
<div class="ui four column grid"> <div class="ten wide column centered"> <div class="ui fluid action input"> <input #input type="text" placeholder="Search for a video..."> </div> </div> </div>
Next, add following code in search-input.component.ts file.
import { Component, ElementRef, EventEmitter, Output, ViewChild, AfterViewInit } from '@angular/core'; import { fromEvent,distinctUntilChanged } from 'rxjs'; import { debounceTime, pluck } from 'rxjs/operators'; @Component({ selector: 'app-search-input', templateUrl: './search-input.component.html', styleUrls: ['./search-input.component.css'] }) export class SearchInputComponent implements AfterViewInit { @ViewChild('input') inputElement!: ElementRef; @Output() search: EventEmitter<string> = new EventEmitter<string>(); constructor() { } ngAfterViewInit() { if (this.inputElement) { fromEvent(this.inputElement.nativeElement, 'keyup') .pipe( debounceTime(500), pluck('target', 'value'), distinctUntilChanged(), ) .subscribe( value => { this.search.emit(this.inputElement.nativeElement.value)} ) } } }
ViewChild (‘input’) gives us access to the input element defined in the HTML file. ‘input’ is a selector that refers to the #input template reference variable we previously added to the input element in the HTML file.
formEvent is used to set up event listeners on a specific element.
debounceTime used to control the rate of user input.
import { Component, Input, OnInit } from '@angular/core'; import { Video } from '../../../shared/models/search.interface'; @Component({ selector: 'app-search-list', templateUrl: './search-list.component.html', styleUrls: ['./search-list.component.css'] }) export class SearchListComponent implements OnInit { @Input() videos: Video[] | undefined; constructor() { } ngOnInit(): void { } }
Here, Input decorator is used to get vidoes list from the parent component.
<div class="ui four column grid"> <div class="column" *ngFor="let video of videos"> <div class="ui card"> <div class="image"> <img [src]="video.thumbnail"> </div> <div class="content"> <a class="header" style="margin: 1em 0 1em 0;">{{ video.title }}</a> <div class="meta"> <span class="date" style="font-weight: bolder;"> <a [href]="video.channelUrl" target="_blank">{{ video.channelTitle }}</a> </span> <span class="ui right floated date" style="font-weight: bolder;">{{ video.publishedAt | date:'mediumDate' }}</span> </div> <div class="description"> {{ video.description?.slice(0,50) }}... </div> </div> <a [href]="video.videoUrl" target="_blank" class="extra content"> <button class="ui right floated tiny red right labeled icon button"> <i class="external alternate icon"></i> Watch </button> </a> </div> </div> </div>
let’s create parent component for search-list component.
ng g c search/search-container
This component should be able to get user inputs from the search-component component. It should pass this over to the search service, which does the operations and returns the expected result. The result should be send to the search-list component, where it will be rendered.
import { Component, OnInit } from '@angular/core'; import { Video } from 'src/app/shared/models/search.interface'; import { SearchService } from 'src/app/shared/services/search.service'; @Component({ selector: 'app-search-container', templateUrl: './search-container.component.html', styleUrls: ['./search-container.component.css'] }) export class SearchContainerComponent { inputTouched = false; loading = false; videos: Video[] = []; constructor(private searchService: SearchService) { } handleSearch(inputValue: string) { this.loading = true; this.searchService.getVideos(inputValue) .subscribe((items : any) => { this.videos = items.map((item => { return { title: item.snippet.title, videoId: item.id.videoId, videoUrl: `https://www.youtube.com/watch?v=${item.id.videoId}`, channelId: item.snippet.channelId, channelUrl: `https://www.youtube.com/channel/${item.snippet.channelId}`, channelTitle: item.snippet.channelTitle, description: item.snippet.description, publishedAt: new Date(item.snippet.publishedAt), thumbnail: item.snippet.thumbnails.high.url }; }); this.inputTouched = true; this.loading = false; }); } }
serch-container.component.html
<div> <app-search-input (search)="handleSearch($event)"></app-search-input> <div *ngIf="inputTouched && !videos.length" class="ui four wide column centered grid" style="margin: 3rem;"> <div class="ui raised aligned segment red warning message"> <i class="warning icon"></i> <span class="ui centered" style="margin: 0 auto;">No Video Found</span> </div> </div> <div *ngIf="loading" style="margin: 3rem;"> <div class="ui active centered inline loader"></div> </div> <app-search-list *ngIf="!loading" [videos]="videos"></app-search-list> </div>
Here, serach servise use the HttpClient, so we have to import HttpClientModule in app.module.ts file.
Paste the following code in app.module.ts file.
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import {HttpClientModule} from '@angular/common/http'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { SearchInputComponent } from './search/components/search-input/search-input.component'; import { SearchListComponent } from './search/components/search-list/search-list.component'; import { SearchContainerComponent } from './search/container/search-container/search-container.component'; @NgModule({ declarations: [ AppComponent, SearchInputComponent, SearchListComponent, SearchContainerComponent ], imports: [ BrowserModule, AppRoutingModule, HttpClientModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
app.component.html
<div class="ui centered grid" style="margin-top: 3rem;"> <div class="fourteen wide column"> <h1 class="ui centered aligned header"> <span style="vertical-align: middle;">Youtube Search </span> <img src="/assets/youtube-icon.png" alt=""> </h1> <app-search-container></app-search-container> </div> </div>
OUTPUT: