onYouTubeIframeAPIReady 未在 angular2 网络应用程序上触发 [英] onYouTubeIframeAPIReady not firing on angular2 web app

查看:17
本文介绍了onYouTubeIframeAPIReady 未在 angular2 网络应用程序上触发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Angular 2、打字稿和 YouTube API 构建一个 Web 应用程序,以便在用户登录后将播放器添加到页面.

I'm building a web application using angular 2, typescript and YouTube API to add a player to the page after the user login.

所以一旦登录,应用就会加载以下组件:

So once logged in, the app loads the following component:

export class MyComponent implements OnInit {
  myService: MyService;

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

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

}

组件html包含以下标签:

The component html contains the following tag:

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

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

And finally, the service has the following:

  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"函数,所以我想知道我应该做些什么不同的事情才能让它正常工作.

I've read that the function "onYouTubeIframeAPIReady" is automatically called by the API, so I wonder what should I do differently to have it working properly.

推荐答案

您需要在全局对象上定义您的函数 onYouTubeIframeAPIReady.这与 JavaScript 的链接答案完全相同.下面是所有 100% JavaScript 的东西,通过其 JavaScript 特性的超集适用于 TypeScript.

You need to define your function, onYouTubeIframeAPIReady, on the global object. This works exactly the same way as in the linked answer for JavaScript. What follows is all 100% JavaScript stuff here, applicable to TypeScript by way of its superset of JavaScript nature.

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

If you are using modules, as is generally the case with an Angular 2 application, then your code is isolated and does not execute in the global scope by default. This means that in order to define a global, we need to obtain a reference to the Global Object. In a browser this is very simple as window refers to the global (unless it is shadowed).

您需要编写的内容非常简单.本质上是

What you need to write is quite straightforward. It is essentially

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

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

That means taking your current code, which looks like this

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 仍会发出代码.

You will get a TypeScript error telling you that window has no definition for onYouTubeIframeAPIReady. This is easily resolved in numerous ways, but I just will illustrate two possibilities, either will do the job, and technically neither is necessary since TypeScript will still emit code in spite of the error.

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

  1. Specify a type assertion on window that suppresses the error

(window as any).onYouTubeIframeAPIReady = function () {}

  • 在 window 上声明成员,以便您可以分配给它而不会出错.在模块内部(回想一下,我们不在全局范围内)我们可以使用以下形式

  • Declare the member on window so that you can assign to it without an error. Inside a module (recall we are not in the global scope) we can use the following form

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

  • 请记住,所有 JavaScript 都是有效的 TypeScript,并且 TypeScript 不会向 JavaScript 添加行为或功能.它是 JavaScript 的一种类型化视图,如果您愿意,也是一种解释,它允许对其进行静态验证并拥有出色的工具、捕捉错误、提供高效的编辑体验,并允许在代码级别记录期望.

    Remember all JavaScript is valid TypeScript and that TypeScript does not add behavior or functionality to JavaScript. It is a typed view, an interpretation if you will, of JavaScript that allows it to be statically verified and have excellent tooling, to catch errors, provide a productive editing experience, and allow expectations to be documented at code level.

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

    This is just JavaScript. It is the very same solution as used in Youtube iframe api not triggering onYouTubeIframeAPIReady, I only posted it because there seemed to be a disconnect.

    附录: 值得注意的是,如果使用 SystemJS 或 RequireJS 等模块加载器,可以通过加载器配置抽象手动脚本标签注入过程.好处是代码更简洁、声明性更强,并且可测试性更高,因为您可以存根 YouTube 依赖项,将您的测试与网络隔离.

    Addendum: It is worth noting that if you using a module loader such as SystemJS or RequireJS, you can abstract the manual script tag injection process via loader configuration. The benefit is cleaner, more declarative code and also increased testability as you can then stub the YouTube dependency, isolating your tests from the network.

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

    For SystemJS, you would use the following configuration

    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,您可以编写

    Now if you wanted to test this code, mocking the YouTube API, you could write

    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);
    });
    

    这篇关于onYouTubeIframeAPIReady 未在 angular2 网络应用程序上触发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆