我正在使用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”,所以我想知道我应该做些什么来让它正常工作。
答案 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仍会发出代码。
在窗口上指定一个类型断言来抑制错误
(window as any).onYouTubeIframeAPIReady = function () {}
在窗口上声明成员,以便您可以在没有错误的情况下为其分配。在模块内部(回想一下我们不在全球范围内),我们可以使用以下形式
declare global {
interface Window {
onYouTubeIframeAPIReady?: () => void;
}
}
请记住,所有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);
});