YouTube Player APIで動画を埋め込むAngularディレクティブを作成する


とあるAngularを使用しているプロジェクトで、YouTubeの動画を埋め込むことタスクがあったのだが、その際にちょっとハマったのでメモ。

とりあえず手っ取り早くYouTubeが提供しているAPIを使用。
iframe組み込みのYouTube Player APIについてはこちら
[https://developers.google.com/youtube/iframe_api_reference?hl=ja](https://developers.google.com/youtube/iframe_api_reference?hl=ja)

## 素のJSで書いた場合
普通に書くなら下記になる。


```
<div id="player"></div>
<script>
  var tag = document.createElement('script');
  tag.src = "https://www.youtube.com/iframe_api";
  var firstScriptTag = document.getElementsByTagName('script')[0];
  firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

  var player;
  function onYouTubeIframeAPIReady() {
    player = new YT.Player('player', {
      height: '390',
      width: '640',
      videoId: 'M7lc1UVf-VE',
      events: {
        'onReady': onPlayerReady,
        'onStateChange': onPlayerStateChange
      }
    });
  }

  function onPlayerReady(event) {
    // event.target.playVideo();
  }

  function onPlayerStateChange(event) {
    if (event.data == YT.PlayerState.ENDED) {
      alert('finished');
    }
  }
  function stopVideo() {
  
  }
</script>
```

## Angularで書いた場合
今回はAngular使ってるので、下記のようなディレクティブを作成する。

```
    var myApp = angular.module('myApp', []);
    
    myApp.directive('youtube', function(){

      var _link = function(scope, element, attr){

        var tag = document.createElement('script');
        tag.src = "https://www.youtube.com/iframe_api";
        var firstScriptTag = document.getElementsByTagName('script')[0];
        firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

        var player;

        function onYouTubeIframeAPIReady() {

          player = new YT.Player(element.children()[0], {
            height: scope.width,
            width: scope.height,
            videoId: scope.youtubeId,
            events: {
              'onStateChange': function(event){
                if (event.data == YT.PlayerState.ENDED){
                  alert('finished!!');
                }
              }
            }
          });
        }

      };

      return {
        link: _link,
        scope: {
          width: "@",
          height: "@",
          youtubeId: "@"
        },
        restrict: 'E',
        template: '<div></div>'
      };
    });

```

htmlは下記の1行を追加

```
<youtube width="640" height="390" youtube-id="M7lc1UVf-VE"></youtube>
```

これでいけるかと思ったが、YouTubeのライブラリを読み込んだ後に実行されるonYouTubeIframeAPIReadyが発火せず・・

ググっていると下記記事を発見。
[http://stackoverflow.com/questions/12256382/youtube-iframe-api-not-triggering-onyoutubeiframeapiready](http://stackoverflow.com/questions/12256382/youtube-iframe-api-not-triggering-onyoutubeiframeapiready)

onYouTubeIframeAPIReady関数はグローバルにしないといけないらしいので、$windowを引数に渡して、

```
$window.onYouTubeIframeAPIReady = function(){}
```
に修正したところ、動きました。

最終的なコードは下記のとおり。


```
    var myApp = angular.module('myApp', []);
    
    myApp.directive('youtube', function($window){

      var _link = function(scope, element, attr){

        var tag = document.createElement('script');
        tag.src = "https://www.youtube.com/iframe_api";
        var firstScriptTag = document.getElementsByTagName('script')[0];
        firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

        var player;

        $window.onYouTubeIframeAPIReady = function() {

          player = new YT.Player(element.children()[0], {
            height: scope.width,
            width: scope.height,
            videoId: scope.youtubeId,
            events: {
              'onStateChange': function(event){
                if (event.data == YT.PlayerState.ENDED){
                  alert('finished!!');
                }
              }
            }
          });
        }

      };

      return {
        link: _link,
        scope: {
          width: "@",
          height: "@",
          youtubeId: "@"
        },
        restrict: 'E',
        template: '<div></div>'
      };
    });

```