How to embed a youtube video in an angular material dialog


Codever Logo

(P) Codever is an open source bookmarks and snippets manager for developers & co. See our How To guides to help you get started. Public bookmarks repos on Github ⭐🙏


It’s easy now to recognise the youtube video bookmarks on www.codever.dev, by placing the youtube logo before the title of the bookmark:

Youtube logo before bookmark title
Bookmarks list with youtube logo

Also, when you click on the youtube logo, a dialog will pop where the video is embedded and you can play it directly there. In this blog post I will show how it is implemented.

As a reminder, the www.codever.dev website uses Angular, with angular material and bootstrap for styling.

## The Bookmarks List Component

HTML Template

In the bookmarks list’s html template I check if the bookmark is a youtube video (a youtubeVideoId must be present), to display the youtube icon

...
  <h5 class="card-title">
    <i *ngIf="bookmark.youtubeVideoId" class="fab fa-youtube youtube-icon" (click)="playYoutubeVideo(bookmark)" title="Play youtube video"></i>
    <a href="" target="_blank" [innerHtml]="bookmark.name | highlight: queryText" (click)="onBookmarkLinkClick(bookmark)"></a>
    <sup class="external-link-hint"><i class="fas fa-external-link-alt"></i></sup>
  </h5>
...

The Angular Bookmarks List Component - AsyncBookmarkListComponent

If the youtube icon is present, when clicked it triggers the method playYoutubeVideo int the AsyncBookmarkListComponent component:

  playYoutubeVideo(bookmark: Bookmark) {
    const dialogConfig = new MatDialogConfig();

    dialogConfig.disableClose = false;
    dialogConfig.autoFocus = true;

    let relativeWidth = (this.innerWidth * 80) / 100; // take up to 80% of the screen size
    if (this.innerWidth > 1500) {
      relativeWidth = (1500 * 80 ) / 100;
    } else {
      relativeWidth = (this.innerWidth * 80 ) / 100;
    }

    const relativeHeight = (relativeWidth * 9) / 16 + 120; // 16:9 to which we add 120 px for the dialog action buttons ("close")
    dialogConfig.width = relativeWidth + 'px';
    dialogConfig.height = relativeHeight + 'px';

    dialogConfig.data = {
      bookmark: bookmark
    };

    const dialogRef = this.deleteDialog.open(PlayYoutubeVideoDialogComponent, dialogConfig);
  }

The configuration of the dialog is quite strait forward. For smaller screens (width < 1500px), the dialog will take up to 80% of the width and the height corresponds to a 16:9 ratio, to which you would add a 120px to properly display the close button. For bigger screen we have fixed values which have the same ratio.

The window width (here innerWidth) is set in the ngOnInit() method of the component:

  ngOnInit(): void {
    this.innerWidth = window.innerWidth;
    ...
  }

Angular Material Dialog Integration

Let’s focus now on the angular material component that displays the youtube video.

HTML Template

The youtube video is embedded via an iframe.

<mat-dialog-content>
  <div class="videoWrapper">
    <iframe [src]='safeUrl' frameborder="0" allowfullscreen></iframe>
  </div>
</mat-dialog-content>

<hr>

<mat-dialog-actions class="app-dialog-actions">
  <button type="button" class="btn btn-primary btn-sm" (click)="close()">Close</button>
</mat-dialog-actions>

To systematically block XSS bugs, Angular treats all values as untrusted by default. So the iframe url needs to be marked as safe, to avoid being sanitized by Angular.

### Dialog Component - PlayYoutubeVideoDialogComponent

The iframe’s url (safeUrl) is marked as trusted by injecting the DomSanitizer and calling its bypassSecurityTrustResourceUrl method:

import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
import { DomSanitizer } from '@angular/platform-browser';
import { Bookmark } from '../../core/model/bookmark';

@Component({
  selector: 'app-play-youtube-video-dialog',
  templateUrl: './play-youtube-video-dialog.component.html',
  styleUrls: ['./play-youtube-video-dialog.component.scss']
})
export class PlayYoutubeVideoDialogComponent implements OnInit {

  bookmark: Bookmark;
  safeUrl: any;

  constructor(
    private dialogRef: MatDialogRef<PlayYoutubeVideoDialogComponent>,
    @Inject(MAT_DIALOG_DATA) data,
    private _sanitizer: DomSanitizer
  ) {
    this.bookmark = data.bookmark;
    this.safeUrl = this._sanitizer.bypassSecurityTrustResourceUrl(`https://www.youtube.com/embed/${this.bookmark.youtubeVideoId}`);
  }

  ngOnInit() {
  }

  close() {
    this.dialogRef.close('Play Youtube Video Closed');
  }

}

CSS

To display the video in a “fluid” manner in the iframe, we use a solution pioneered by Thierry Koblentz and presented on A List Apart in 2009: Creating Intrinsic Ratios for Video:

“The idea is to create a box with the proper ratio ( 16:9, etc.), then make the video inside that box stretch to fit the dimensions of the box. It’s that simple.” I recommend you read the whole article to understand the details.

.videoWrapper {
  position: relative;
  padding-bottom: 56.25%; /* 16:9 */
  padding-top: 25px;
  height: 0;
}
.videoWrapper iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

Conclusion

Well it as simple as that. The whole code source for this for www.codever.dev is available on Github.

I would really appreciate if you gave www.codever.dev a try and have a look at the generated public bookmarks at https://github.com/CodeverDotDev/bookmarks.

Subscribe to our newsletter for more code resources and news

Adrian Matei (aka adixchen)

Adrian Matei (aka adixchen)
Life force expressing itself as a coding capable human being

routerLink with query params in Angular html template

routerLink with query params in Angular html template code snippet Continue reading