onYouTubeIframeAPIReady 未在 angular2 网络应用程序上触发
onYouTubeIframeAPIReady not firing on angular2 web app
我正在使用 angular 2、typescript 和 YouTube API 构建一个网络应用程序,以便在用户登录后向页面添加播放器。
因此,一旦登录,应用程序就会加载以下组件:
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);
}
我读到函数 "onYouTubeIframeAPIReady" 由 API 自动调用,所以我想知道我应该做些什么不同的事情才能让它正常工作。
您需要在全局对象上定义函数 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 上指定类型断言以抑制错误
(window as any).onYouTubeIframeAPIReady = function () {}
在 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);
});
我正在使用 angular 2、typescript 和 YouTube API 构建一个网络应用程序,以便在用户登录后向页面添加播放器。
因此,一旦登录,应用程序就会加载以下组件:
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);
}
我读到函数 "onYouTubeIframeAPIReady" 由 API 自动调用,所以我想知道我应该做些什么不同的事情才能让它正常工作。
您需要在全局对象上定义函数 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 上指定类型断言以抑制错误
(window as any).onYouTubeIframeAPIReady = function () {}
在 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);
});