【问题标题】:onYouTubeIframeAPIReady not firing on angular2 web apponYouTubeIframeAPIReady 未在 angular2 网络应用程序上触发
【发布时间】:2017-08-09 15:25:55
【问题描述】:

我正在使用 Angular 2、打字稿和 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”函数,所以我想知道我应该做些什么不同的事情才能让它正常工作。

【问题讨论】:

  • @AluanHaddad 我以前见过这个问题,我试图遵循它的逻辑,但是我无法将它适应打字稿,这就是为什么我在这里要求指定这些细节。
  • 我不确定这与 TypeScript 有什么关系。如果它在 JavaScript 中工作,那么它在 TypeScript 中的工作方式相同。这完全扩展到范围,这似乎是这里的问题。不要将 TypeScript 视为 JavaScript 的替代品,这一点非常重要,因为它根本不是那种东西。
  • 归结为 YouTube API 调用了一个名为 onYouTubeIframeAPIReady 的全局函数。您在服务中定义的同名方法是完全任意的。 YouTube API 将如何调用该方法?
  • @AluanHaddad 好的,我现在明白了,但是如何使用 typescript 使其全局化?

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


【解决方案1】:

您需要在全局对象上定义您的函数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. 在 window 上声明成员,以便您可以分配给它而不会出错。在模块内部(回想一下,我们不在全局范围内)我们可以使用以下形式

    declare global {
      interface Window {
        onYouTubeIframeAPIReady?: () => void;
      }
    }
    

请记住,所有 JavaScript 都是有效的 TypeScript,并且 TypeScript 不会向 JavaScript 添加行为或功能。它是 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,您可以编写

test/test-stubs/stub-youtube-api.ts

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

test/services/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);
});

【讨论】:

  • 非常感谢!您刚刚向我澄清了很多,这对我有很大帮助!它完美地工作。非常感谢,谢谢。
  • 很高兴我能提供帮助
猜你喜欢
  • 2014-05-24
  • 1970-01-01
  • 2012-08-28
  • 2012-11-18
  • 1970-01-01
  • 2016-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多