因此,一旦登录,该应用程序将加载以下组件:
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”,所以我想知道我应该做些什么来让它正常工作.
接下来是这里所有100%的JavaScript内容,通过其JavaScript性质的超集适用于TypeScript.
如果您正在使用模块(通常是Angular 2应用程序的情况),那么您的代码将被隔离,并且默认情况下不会在全局范围内执行.这意味着为了定义全局,我们需要获得对全局对象的引用.在浏览器中,这非常简单,因为窗口指的是全局(除非它被遮蔽).
你需要写的东西非常简单.它本质上是
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错误,告诉您该窗口没有onYouTubeIframeAPIReady的定义.这很容易通过多种方式解决,但我只是说明两种可能性,要么就是完成工作,技术上也没有必要,因为尽管出现错误,TypeScript仍然会发出代码.
>在窗口上指定一个类型断言来抑制错误
(window as any).onYouTubeIframeAPIReady = function () {}
>在窗口上声明成员,以便您可以分配给它而不会出现错误.在模块内部(回想一下,我们不在全球范围内),我们可以使用以下形式
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,您可以写
测试/检验存根/存根的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); });