onYouTubeIframeAPIReady没有在angular2 web应用程序上触发

时间:2017-03-17 22:45:54

标签: angular typescript youtube-api youtube-iframe-api

我正在使用angular 2,typescript和YouTube API构建一个Web应用程序,以便在用户登录后将播放器添加到页面中。

因此,一旦登录,该应用程序将加载以下组件:

export class MyComponent implements OnInit {
  myService: MyService;

  constructor( private _myService: MyService ) {
    this.myService = _myService;
  }

  ngOnInit() {
   this._myService.loadAPI();
  }

}

组件html包含以下标记:

<iframe id="player" type="text/html" width="640" height="360"
      src="http://www.youtube.com/embed/M7lc1UVf-VE?enablejsapi=1"
      frameborder="0" allowfullscreen></iframe>

最后,该服务具有以下内容:

  player: YT.Player;

  loadAPI(){
    var tag = document.createElement('script');
    tag.src = "https://www.youtube.com/iframe_api";
    var firstScriptTag = document.getElementsByTagName('script')[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
    console.log('API loaded'); // this is shown on the console.
  }

  onYouTubeIframeAPIReady(){
    this.player = new YT.Player('player', {
      events: {
        'onReady': this.onPlayerReady,
        'onStateChange': this.onPlayerStateChange
      }
    });
    console.log('youtube iframe api ready!'); // this is never triggered.
  }

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

  onPlayerStateChange(status){
    console.log(status.data);
  }  

我已经读过API自动调用函数“onYouTubeIframeAPIReady”,所以我想知道我应该做些什么来让它正常工作。

1 个答案:

答案 0 :(得分:5)

您需要在全局对象上定义函数onYouTubeIframeAPIReady。这与JavaScript的链接答案完全相同。 接下来是这里所有100%的JavaScript内容,通过其JavaScript性质的超集来适用于TypeScript。

如果您正在使用模块,就像Angular 2应用程序的情况一样,那么您的代码是隔离的,默认情况下不会在全局范围内执行。这意味着为了定义全局,我们需要获得对全局对象的引用。在浏览器中,这非常简单,因为window指的是全局(除非它被遮蔽)。

你需要写的东西非常简单。它基本上是

window.onYouTubeIframeAPIReady = function () { ... };

这意味着获取当前代码,如下所示

export default class YouTubeService {
  ...
  loadAPI() {
    var tag = document.createElement('script');
    tag.src = "https://www.youtube.com/iframe_api";
    var firstScriptTag = document.getElementsByTagName('script')[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
    console.log('API loaded'); // this is shown on the console.
  }

  onYouTubeIframeAPIReady() { }
}

将其改为此

export default class YouTubeService {
  ...
  loadAPI() {
    window.onYouTubeIframeAPIReady = function () { };

    var tag = document.createElement('script');
    tag.src = "https://www.youtube.com/iframe_api";
    var firstScriptTag = document.getElementsByTagName('script')[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
    console.log('API loaded'); // this is shown on the console.
  }
}

您将收到TypeScript错误,告知您window没有onYouTubeIframeAPIReady的定义。这很容易通过多种方式解决,但我只会说明两种可能性,要么会完成工作,要么技术上都不需要,因为尽管出现错误,TypeScript仍会发出代码。

  1. 在窗口上指定一个类型断言来抑制错误

    (window as any).onYouTubeIframeAPIReady = function () {}
    
  2. 在窗口上声明成员,以便您可以在没有错误的情况下为其分配。在模块内部(回想一下我们不在全球范围内),我们可以使用以下形式

    declare global {
      interface Window {
        onYouTubeIframeAPIReady?: () => void;
      }
    }
    
  3. 请记住,所有JavaScript都是有效的TypeScript,并且TypeScript不会向JavaScript添加行为或功能。它是一种类型化的视图,如果你愿意,可以解释它允许静态验证并具有出色的工具,捕获错误,提供高效的编辑体验,并允许在代码级别记录期望。

    这只是JavaScript。它与Youtube iframe api not triggering onYouTubeIframeAPIReady中使用的解决方案完全相同,我只发布它,因为似乎有断开连接。

    附录:值得注意的是,如果使用SystemJS或RequireJS等模块加载程序,则可以通过加载程序配置抽象手动脚本标记注入过程。这样做的好处是更清晰,更具声明性的代码以及更高的可测试性,因为您可以将YouTube依赖性存根,将测试与网络隔离开来。

    对于SystemJS,您将使用以下配置

    SystemJS.config({
      map: {
        "youtube": "https://www.youtube.com/iframe_api"
      },
      meta: {
        "https://www.youtube.com/iframe_api": {
          "format": "global",
          "scriptLoad": true,
          "build": false
        }
      }
    });
    

    你可以写

    export default class YouTubeService {
      async loadAPI() {
        window.onYouTubeIframeAPIReady = function () {
          console.log('API loaded'); // this is shown on the console.
        };
        try {
          await import('youtube'); // automatically injects a script tag
        }
        catch (e) {
          console.error('The YouTube API failed to load');
        }
      }
    }
    
    declare global {
      interface Window {
        onYouTubeIframeAPIReady?: () => void;
      }
    }
    

    现在,如果您想测试此代码,模拟YouTube API,您可以编写

    <强>测试/测试存根/存根的YouTube-api.ts

    (function () {
      window.onYouTubeIframeAPIReady();
    }());
    

    <强>测试/服务/ YouTube的-service.spec.ts

    import test from 'blue-tape';
    
    import YouTubeService from 'src/services/youtube.service'
    
    SystemJS.config({
      map: {
        "youtube": "test/test-stubs/stub-youtube-api.ts"
      }
    });
    
    if(typeof window === 'undefined') {
      global.window = {};
    }
    
    
    test('Service must define a callback for onYouTubeIframeAPIReady', async ({isNot}) => {
      const service = new YouTubeService();
      await service.loadAPI();
      t.isNot(undefined, window.onYouTubeIframeAPIReady);
    });