onAngular2 Web应用程序上未触发onYouTubeIframeAPIReady [英] onYouTubeIframeAPIReady not firing on angular2 web app

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

问题描述

我正在使用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 () {}

  • 在窗口上声明该成员,以便您可以对其进行分配而不会出现错误.在模块内部(回想一下我们不在全局范围内),我们可以使用以下形式

  • 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不会触发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);
    });
    

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

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