Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Youtube API with Angular 6 (Typescript)

I'm able to get the Youtube Iframe API (somehow) working in my Angular 6 application. However, I would like to however feed it dynamic videoIds from a list that I control. My plan is to listen to events from the player and then load another video once its done playing the current video. But I could not seem to get past this issue. I'm still learning Typescript and Angular and I'm curious as to why my any method that I call from any function that I call from within the IFrame API player gets an Uncaught TypeError:

Here is my code:

import { Component, OnInit, AfterViewInit } from '@angular/core';
import { Video } from '../shared/model/video.model';
import { Subscription } from 'rxjs';
import { VideoPlayerService } from '../shared/services/video-player.service';

@Component({
  selector: 'ntv-video-player',
  templateUrl: './video-player.component.html',
  styleUrls: ['./video-player.component.css']
})
export class VideoPlayerComponent implements OnInit, AfterViewInit {

  player;
  video: Video;
  videos: Video[];
  videoSubscription: Subscription;

  constructor(private videoService: VideoPlayerService) { }

  ngAfterViewInit() {
    const doc = (<any>window).document;
    const playerApiScript = doc.createElement('script');
    playerApiScript.type = 'text/javascript';
    playerApiScript.src = 'https://www.youtube.com/iframe_api';
    doc.body.appendChild(playerApiScript);
  }

  ngOnInit() {

    (<any>window).onYouTubeIframeAPIReady = () => {
      this.player = new (<any>window).YT.Player('player', {
        height: '100%',
        width: '100%',
        // videoId: this.getVideo(),
        events: {
          'onReady': this.onPlayerReady,
          'onStateChange': this.onPlayerStateChange
        },
        playerVars: {
          autoplay: 1,
          controls: 0,
          modestbranding: 1,
          // playlist: 'UG3sfZKtCQI,ALZHF5UqnU4,x9ZkC3OgI78',
          rel: 0,
          showInfo: 0
        }
      });
    };
  }



  // The API calls this function when the player's state changes.
  onPlayerStateChange(event) {
    console.log('onPlayerStateChange');
    console.log(event.data);
  }

  // The API will call this function when the video player is ready
  onPlayerReady(event) {
    console.log(event);

    const videoId = this.getVideo();
    event.target.cueVideoById({
      'videoId': videoId
    });
    event.target.playVideo();
  }

  getVideo() {
    return '60ItHLz5WEA';
  }

}

And here is the error that I'm getting:

    Uncaught TypeError: Cannot read property 'getVideo' of undefined
    at push../src/app/video-player/video-player.component.ts.VideoPlayerComponent.onPlayerReady (video-player.component.ts:63)
    at M.h.G (www-widgetapi.js:48)
    at Y.h.o (www-widgetapi.js:92)
    at Y.h.H (www-widgetapi.js:105)
    at Wa.g (www-widgetapi.js:81)
    at Oa.f (www-widgetapi.js:70)
    at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:421)
    at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:188)
    at ZoneTask.push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask [as invoke] (zone.js:496)
    at invokeTask (zone.js:1540)
push../src/app/video-player/video-player.component.ts.VideoPlayerComponent.onPlayerReady    @   video-player.component.ts:63
h.G @   www-widgetapi.js:48
h.o @   www-widgetapi.js:92
h.H @   www-widgetapi.js:105
Wa.g    @   www-widgetapi.js:81
Oa.f    @   www-widgetapi.js:70
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask    @   zone.js:421
push../node_modules/zone.js/dist/zone.js.Zone.runTask   @   zone.js:188
push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask    @   zone.js:496
invokeTask  @   zone.js:1540
globalZoneAwareCallback

this.getVideo() seems to work if I call it from the 'videoId' property, but I'd like to be able to call it either onPlayerReady or onPlayerStateChange so I can make it more dynamic. Anybody have done this before? There's not a lot of documentation for Angular.

Thanks in advance.

like image 297
Noel R. Avatar asked Sep 11 '18 03:09

Noel R.


2 Answers

I think what your experiencing is a scope issue, your OnPlayerReady function is not being executed in the scope of your class, but rather the scope of the youtube player. I'm inferring this from the error message "Cannot read property 'getVideo' of undefined". At this point of execution "this" is not what it appears to be.

Try changing your event assignment code like this:

 events: {
          'onReady': (event) => { this.onPlayerReady(event); },
          'onStateChange': (event) => { this.onPlayerStateChange(event); }
        },
like image 191
Joel Richman Avatar answered Sep 18 '22 11:09

Joel Richman


Thanks Joel! This help me get past the issue. I did get an error at first because I was not passing a parameter. So I did it like this and it worked!

events: { 'onReady': (event) => { this.onPlayerReady(event); }, 
'onStateChange': (event) => { this.onPlayerStateChange(event); } 
},

I guess I'm having a hard time understanding the API's documentation and trying to translate it to Typescript. I just wish they'll add more documentation there. Also doing things like this (<any>window).onYouTubeIframeAPIReady = () => { just feels kinda hacky to me. Is there a better way of doing this?

Sorry I couldn't upvote your answer since I don't have enough rep points yet.

like image 38
Noel R. Avatar answered Sep 19 '22 11:09

Noel R.