How to add a YouTube Video Player

Luis Aviles

YouTube is, without any doubt, one of the most popular websites for video-sharing today. It's really easy to upload any video format and share it with your friends. Also, the YouTube team provides an API Reference to let you embed a YouTube video on any website and, of course, control it using JavaScript.

Some time ago, I started working on a web application using the Angular framework, and one of the main features involved the use of an embedded YouTube video player to avoid the user's context switching, or opening a separate browser to see a video. At that time, there was no native solution to solve this problem like there is today.

In this MediaJam we'll leverage a TypeScript-oriented solution, using the latest Angular version, to build a small web application, and embed a YouTube video player.

Prerequisites

You'll need to have installed the following tools in your local environment:

  • The latest LTS version of Node.js available is recommended.
  • Either NPM or Yarn as a package manager.
  • The Angular CLI tool (Command-line interface for Angular).

Initialize the Project

Let's create a small project from scratch using the Angular CLI tool.

First, you can create the folder using mkdir angular-youtube-player-demo.

Then, enter the newly created folder, and run the ng new tool following the below syntax:

1ng new <project-name> [options]

The ng new command will take the project-name as an input to have an application ready following best practices. In this case, let's use the directory we just created above.

1cd angular-youtube-player-demo
2ng new angular-youtube-player-demo --directory ./ --routing --prefix corp --style css --minimal

This command will initialize a base project, using some configuration options:

  • angular-youtube-player-demo. Sets the name of the Angular project.
  • --directory ./. Sets the directory to generate the files.
  • --routing. Creates a routing module.
  • --prefix corp. Defines a prefix to be applied to the selectors for created components(corp in this case). The default value is app.
  • --style css. The file extension for the styling files.
  • --minimal. Creates the project without any testing framework. Useful when you're working on a proof-of-concept project, for example.

The output of the previous command will be as follows.

1CREATE README.md (1033 bytes)
2CREATE .gitignore (631 bytes)
3CREATE angular.json (3163 bytes)
4CREATE package.json (775 bytes)
5CREATE tsconfig.json (538 bytes)
6CREATE .browserslistrc (703 bytes)
7CREATE tsconfig.app.json (287 bytes)
8CREATE src/favicon.ico (948 bytes)
9CREATE src/index.html (312 bytes)
10CREATE src/main.ts (372 bytes)
11CREATE src/polyfills.ts (2830 bytes)
12CREATE src/styles.css (80 bytes)
13CREATE src/assets/.gitkeep (0 bytes)
14CREATE src/environments/environment.prod.ts (51 bytes)
15CREATE src/environments/environment.ts (662 bytes)
16CREATE src/app/app-routing.module.ts (245 bytes)
17CREATE src/app/app.module.ts (393 bytes)
18CREATE src/app/app.component.ts (1503 bytes)
19✔ Packages installed successfully.

If you pay attention to the generated files and directories, you'll see a minimal project structure for the source code too:

1|- src/
2 |- app/
3 |- app.module.ts
4 |- app-routing.module.ts
5 |- app.component.ts

Video Player Implementation

Install the YouTube Player Package

Before embedding any video in our application, install the @angular/youtube-player package.

1npm install --save @angular/youtube-player

If you use Yarn, instead run

1yarn add @angular/youtube-player

Import the YouTubePlayer Module

Open the app.module.ts file and import the YouTubePlayerModule

1// ... other imports
2import {YouTubePlayerModule} from '@angular/youtube-player';
3
4@NgModule({
5 declarations: [
6 //...
7 ],
8 imports: [
9 BrowserModule,
10 AppRoutingModule,
11 YouTubePlayerModule
12 ],
13 providers: [],
14 bootstrap: [AppComponent]
15})
16export class AppModule { }

This can be done in a separate module if that's the case in your implementation.

Define the Data Model

Let's define the data model by creating a new file in app/model folder. We can use the Angular CLI tool for that:

1ng generate interface model/video
2CREATE src/app/model/video.ts (28 bytes)

Then, open the brand new file video.ts in your editor, and set the model using a TypeScript interface.

1export interface Video {
2 title: string;
3 link: string;
4}

Create the Video Player Component

Let's create the video-player component, again, using the Angular CLI tool:

1ng generate component video-player
2CREATE src/app/video-player/video-player.component.ts (283 bytes)
3UPDATE src/app/app.module.ts (497 bytes)

Now, open the video-player.component.ts file, and add the following TypeScript code:

1export class VideoPlayerComponent implements OnInit {
2 private apiLoaded = false;
3
4 @Input() videoId: string;
5
6 constructor() { }
7
8 ngOnInit(): void {
9 if(!this.apiLoaded) {
10 const tag = document.createElement('script');
11 tag.src = 'https://www.youtube.com/iframe_api';
12 document.body.appendChild(tag);
13 this.apiLoaded = true;
14 }
15 }
16}

The ngOnInit() function is part of the component lifecycle, and it will be called once the data property(videoId) has been initialized.

Since the @angular/youtube-player package defines a component wrapper based on the YouTube player API, it needs to load the IFrame Player asynchronously.

The method used in the previous code snippet access the DOM, and download the API code through the <script> tag. It would be something like this:

1<script src="https://www.youtube.com/iframe_api"></script>

Next, define the template along with the Property binding through videoId as follows.

1<youtube-player [videoId]="videoId"></youtube-player>

Using the Video Player Component

Let's create a couple of video entries to be rendered through the Video Player Component we just created.

Open the app.component.ts file, and set a videoList attribute.

1// ... other imports
2import { Video } from './model/video';
3
4export class AppComponent {
5 videoList: Video[] = [
6 {
7 title: '[Debugging] Expression has changed after it was checked',
8 link: 'https://www.youtube.com/watch?v=O47uUnJjbJc'
9 },
10 {
11 title: '[Debugging] The pipe {name} could not be found',
12 link: 'https://www.youtube.com/watch?v=maI2u6Sxk9M'
13 }
14 ];
15}

The videoList attribute contains a set of Video objects with the title and URL.

Next, let's render a list of videos, and use the <corp-video-player> component. Open the app.component.ts file, and update the template section.

1<div style="text-align:center">
2 <h1>
3 Angular YouTube Video Player Demo
4 </h1>
5</div>
6<h2>Select a Video</h2>
7<ul>
8 <li *ngFor="let video of videoList">
9 <h2><a href="#" (click)="selectVideo(video)">{{video.title}}</a></h2>
10 </li>
11</ul>
12<corp-video-player [videoId]="currentVideoId"></corp-video-player>

As you can see, there are two bindings here: An Event binding((click)="selectVideo(video)") and a Property Binding([videoId]="currentVideoId"). Let's define both of them to have the final code in the app.component.ts file.

1export class AppComponent {
2 videoList: Video[] = [
3 // Objects defined above
4 ];
5
6 currentVideoId: string;
7
8 selectVideo(video: Video) {
9 const params = new URL(video.link).searchParams;
10 this.currentVideoId = params.get('v');
11 }
12}

Pay attention to the selectVideo() function implementation, since it "extracts" the video identifier from a URL object. Then, the Id value is assigned to the currentVideoId property.

Every time the currentVideoId gets changed, the <corp-video-player> will be updated because of the Property binding.

The YouTube Video Player API

Properties

NameDescription
@Input() videoId: stringThe YouTube video identifier to be rendered
@Input() width: numberThe width of the video player
@Input() height: numberThe height of the video player
@Input() startSeconds: numberThe moment when the player is supposed to start playing
@Input() endSeconds: numberThe moment when the player is supposed to stop playing
@Input() suggestedQuality: YT.SuggestedVideoQualityThe suggested quality of the player
@Input() playerVars: YT.PlayerVarsExtra parameters used to configure the player. See here
@Input() showBeforeIframeApiLoads: booleanWhether the iFrame will attempt to load regardless of the status of the API on the page.

Events

Name
@Output() ready: Observable<YT.PlayerEvent>
@Output() stateChange: Observable<YT.OnStateChangeEvent>
@Output() error: Observable<YT.OnErrorEvent>
@Output() apiChange: Observable<YT.PlayerEvent>
@Output() playbackQualityChange: Observable<YT.OnPlaybackQualityChangeEvent>
@Output() playbackRateChange: Observable<YT.OnPlaybackRateChangeEvent>

Learn more about the available properties and events here.

Live Demo

Find the source code available in GitHub.

If you prefer, you can play around with the project in CodeSandbox too:

Feel free to reach out on Twitter if you have any questions. Follow me on GitHub to see more about my work.

Luis Aviles

Senior Software Engineer

Luis is a Senior Software Engineer and Google Developer Expert in Web Technologies and Angular. He is an author of online courses, technical articles, and a public speaker. He has participated in different international technology conferences, giving technical talks, workshops, and training sessions. He’s passionate about the developer community and he loves to help junior developers and professionals to improve their skills.
When he’s not coding, Luis is doing photography or Astrophotography.