我从模型中调用事件时不断收到以下错误:
window.Playlist = new PlaylistModel();
Playlist.trigger("playerPlaying");
`Uncaught TypeError: Cannot read property '0' of undefined`
我的堆栈跟踪:
Uncaught TypeError: Cannot read property '0' of undefined
triggerEventsbackbone.js:206
Backbone.Events.triggerbackbone.js:148
onPlayerStateChangeplayer.js:101
g.Iwww-widgetapi-vfldqBTcy.js:13
g.kwww-widgetapi-vfldqBTcy.js:22
g.Jwww-widgetapi-vfldqBTcy.js:30
X.dwww-widgetapi-vfldqBTcy.js:29
Qa.fwww-widgetapi-vfldqBTcy.js:18
我挖掘骨干源代码并找到以下内容
trigger: function(name) {
if (!this._events) return this;
var args = slice.call(arguments, 1);
if (!eventsApi(this, 'trigger', name, args)) return this;
var events = this._events[name];
var allEvents = this._events.all;
if (events) triggerEvents(events, args);
if (allEvents) triggerEvents(allEvents, arguments);
return this;
在从chrome控制台设置断点后,我逐行打印了这些行。
arguments = ["playerPlaying"]
args = []
this._events = Object {change:currentSong: Array[3], change:loopActive: Array[3], playerPlaying: Array[3]}
events = [Object, Object, Object]
this = the Backbone model on which `trigger()` was called
我认为问题是arguments
应该是[ModelThatTriggeredEvent,eventName],但我只是[eventName],所以args
变成一个空数组。有谁知道为什么会这样?
更新
这是整个 PlaylistModel ,其中部分内容已删除。请理解代码中的混乱,因为我正在重构它以遵守Backbone的做事方式。
define([
// These are path alias that we configured in our bootstrap
'jquery', // lib/jquery/jquery
'backbone',
'../../common/models/song',
'../collections/songs'
], function($, Backbone, Song, Songs){
window.AVAILABLE_CHARTS = {
billboardChart: {
source: "billboard",
chart: [
{genre: chrome.i18n.getMessage("pop"), url: 'http://www.billboard.com/rss/charts/hot-100'},
{genre: chrome.i18n.getMessage("rock"), url: "http://www.billboard.com/rss/charts/rock-songs"},
]
}
};
var Playlist = Backbone.Model.extend({
defaults: {
currentSong: null,
nextSong: null,
prevSong: null,
genre: null, // initial genre
loopActive: false,
shuffleActive: false,
numSongs: 10, // initial number of songs loaded
musicChart: null
},
initialize: function() {
// Setting collections/songs as its attribute
var songs = new Songs();
this.set('songs', songs);
var userLocale = chrome.i18n.getMessage("@@ui_locale");
if (userLocale == "ko" || userLocale == 'ko-kr') {
this.set('musicChart', AVAILABLE_CHARTS.melonChart);
this.set('genre', this.get('musicChart').chart[0].genre);
} else {
this.set('musicChart', AVAILABLE_CHARTS.billboardChart);
this.set('genre', this.get('musicChart').chart[0].genre);
}
},
// If loop is active, getNextSong repeats the current song
// If shuffle is active, getNextSong plays a random song from Songs
getNextSong: function() {
//var idx = this.indexOf(this.getCurrentSong());
var songs = this.get('songs');
var idx = songs.indexOf(songs.findWhere({ title: this.get('currentSong').get('title') }));
if (this.get('loopActive')) {
return songs.at(idx);
}
if (this.get('shuffleActive')) {
var randomIndex = Math.floor((Math.random()*songs.length));
return songs.at(randomIndex);
}
if (idx != songs.length-1) return songs.at(idx+1);
else return songs.at(0);
},
getPrevSong: function() {
var songs = this.get('songs');
var idx = songs.indexOf(songs.findWhere({ title: this.get('currentSong').get('title') }));
if (idx != 0) return songs.at(idx-1);
else return songs.at(songs.length-1);
},
// Get new songs from Billboard Chart
// First parse the top <numSongs> from the selected <genre>
// from Billboard, and then use YouTube gdata api to fetch
// the songs.
getNewSongs: function (callback, genre, numSongs) {
// FIXME: just trigger progress
var popupWindow = chrome.extension.getViews({ type: "popup" })[0];
if (popupWindow && popupWindow.popupView) popupWindow.popupView.setProgress(10);
var playlist = this;
playlist.get('songs').reset();
// Inspect Billboard Chart to find pop songs
$.get(url+numSongs+'/explicit=true/xml', function (data) {
var popupWindow = chrome.extension.getViews({ type: "popup" })[0];
if (popupWindow && popupWindow.popupView) popupWindow.popupView.setProgress(30);
var $feed = $(data).find('feed')
var $entries = $feed.find('entry')
var numAvailableSongs = $entries.length;
$entries.each(function (idx, entry) {
var title_artist_pair = $(entry).find('title')[0].innerHTML;
var title = $.trim(title_artist_pair.split(' - ')[0]);
var artist = $.trim(title_artist_pair.split(' - ')[1]);
var rank = idx+1;
var query = title + " " + artist;
_searchYouTube(title, artist, rank, query, numAvailableSongs);
});
return;
});
}
function _searchYouTube (title, artist, rank, query, numAvailableSongs) {
var songs = playlist.get('songs');
$.ajax({
url: searchUrl,
type: "GET",
data: 'q='+encodeURIComponent(query),
success: function (result) {
//console.log("\n**** 검색: "+query);
ajaxCount += 1;
if (result.items.length)
var videoId = result.items[0].id.videoId;
else
var videoId = null; // Cannot find the song on YouTube
var song = new Song({
title: title,
artist: artist,
rank: parseInt(rank),
query: query,
videoId: videoId
});
// Insert songs into the playlist in the order of their ranks
// *Note: Songs that do not exist on YouTube are ignored
if (videoId) songs.add(song, { silent: true });
// All the ajax calls are finished
if (ajaxCount == numAvailableSongs) {
var popupWindow = chrome.extension.getViews({ type: "popup" })[0];
if (popupWindow && popupWindow.popupView) popupWindow.popupView.setProgress(70);
songs.comparator = 'rank'
songs.sort();
// Remove useless playlsit methods
if (!playlist.get('currentSong')) {
playlist.set('currentSong', songs.at(0));
}
callback();
}
},
error: function (error) {
ajaxCount += 1;
if (ajaxCount == numAvailableSongs) {
var popupWindow = chrome.extension.getViews({ type: "popup" })[0];
if (popupWindow) popupWindow.popupView.setProgress(70);
if (!playlist.get('currentSong')) {
playlist.set('currentSong', songs.at(0));
}
callback();
}
} // end of error
}); // end of second ajax
} // end of _searchYouTube()
},
lookUpAndAddSingleSong: function (query) {
var playlist = this;
var youtubeAPIKey = "fdf";
var initialSearchURL = "df";
var searchUrl = initialSearchURL + "&key=" + youtubeAPIKey;
$.ajax({
url: searchUrl,
type: "GET",
data: 'q='+encodeURIComponent(query),
success: function (result) {
if (result.items.length) {
var videoId = result.items[0].id.videoId;
var song = new Song({
title: result.items[0].snippet.title,
query: query,
videoId: videoId
});
} else var videoId = null; // Cannot find the song on YouTube
if (videoId) {
playlist.get('songs').add(song);
song.save(); // save to localStorage
}
}, error: function (xhr, status, errorThrown) {
var errorMessage = "lookUpAndAddSingleSong error: check http://instantmusicapp.com";
var popupWindow = chrome.extension.getViews({ type: "popup" })[0];
if (popupWindow && popupWindow.popupView) popupWindow.showErrorMessage(errorMessage);
return;
}
});
},
setMusicChart: function (chartName) {
// if the chart is provided, we pick from Melon, Billboard, iTunes
if (chartName) {
if (chartName == chrome.i18n.getMessage("melonChart"))
this.set('musicChart', AVAILABLE_CHARTS.melonChart);
else if (chartName == chrome.i18n.getMessage("billboardChart"))
this.set('musicChart', AVAILABLE_CHARTS.billboardChart);
else
this.set('musicChart', AVAILABLE_CHARTS.itunesChart);
// else, the user is looking for a personal favorite chart
} else {
this.set('musicChart', AVAILABLE_CHARTS.myChart);
}
},
});
return Playlist;
});
答案 0 :(得分:0)
您的模型中存在相当多的语法错误,这可能是您无法使用bb模型发出触发器甚至是其他任何内容的原因。
第14行有一个悬空逗号(,)。例如。
尝试通过JSLint或JSHint运行模型并修复错误。可能会解决你的问题
答案 1 :(得分:0)
在大多数浏览器中都可以使用悬空逗号,但实际错误是}
函数后的额外getNewSongs
。我建议使用一些支持JSHint集成的IDE / Editor。你可以试试webstorm。这是更新的代码。
define([
// These are path alias that we configured in our bootstrap
'jquery', // lib/jquery/jquery
'backbone',
'../../common/models/song',
'../collections/songs'
], function ($, Backbone, Song, Songs) {
window.AVAILABLE_CHARTS = {
billboardChart: {
source: "billboard",
chart: [
{genre: chrome.i18n.getMessage("pop"), url: 'http://www.billboard.com/rss/charts/hot-100'},
{genre: chrome.i18n.getMessage("rock"), url: "http://www.billboard.com/rss/charts/rock-songs"},
]
}
};
var Playlist = Backbone.Model.extend({
defaults: {
currentSong: null,
nextSong: null,
prevSong: null,
genre: null, // initial genre
loopActive: false,
shuffleActive: false,
numSongs: 10, // initial number of songs loaded
musicChart: null
},
initialize: function () {
// Setting collections/songs as its attribute
var songs = new Songs();
this.set('songs', songs);
var userLocale = chrome.i18n.getMessage("@@ui_locale");
if (userLocale == "ko" || userLocale == 'ko-kr') {
this.set('musicChart', AVAILABLE_CHARTS.melonChart);
this.set('genre', this.get('musicChart').chart[0].genre);
} else {
this.set('musicChart', AVAILABLE_CHARTS.billboardChart);
this.set('genre', this.get('musicChart').chart[0].genre);
}
},
// If loop is active, getNextSong repeats the current song
// If shuffle is active, getNextSong plays a random song from Songs
getNextSong: function () {
//var idx = this.indexOf(this.getCurrentSong());
var songs = this.get('songs');
var idx = songs.indexOf(songs.findWhere({ title: this.get('currentSong').get('title') }));
if (this.get('loopActive')) {
return songs.at(idx);
}
if (this.get('shuffleActive')) {
var randomIndex = Math.floor((Math.random() * songs.length));
return songs.at(randomIndex);
}
if (idx != songs.length - 1) return songs.at(idx + 1);
else return songs.at(0);
},
getPrevSong: function () {
var songs = this.get('songs');
var idx = songs.indexOf(songs.findWhere({ title: this.get('currentSong').get('title') }));
if (idx != 0) return songs.at(idx - 1);
else return songs.at(songs.length - 1);
},
// Get new songs from Billboard Chart
// First parse the top <numSongs> from the selected <genre>
// from Billboard, and then use YouTube gdata api to fetch
// the songs.
getNewSongs: function (callback, genre, numSongs) {
// FIXME: just trigger progress
var popupWindow = chrome.extension.getViews({ type: "popup" })[0];
if (popupWindow && popupWindow.popupView) popupWindow.popupView.setProgress(10);
var playlist = this;
playlist.get('songs').reset();
// Inspect Billboard Chart to find pop songs
$.get(url + numSongs + '/explicit=true/xml', function (data) {
var popupWindow = chrome.extension.getViews({ type: "popup" })[0];
if (popupWindow && popupWindow.popupView) popupWindow.popupView.setProgress(30);
var $feed = $(data).find('feed')
var $entries = $feed.find('entry')
var numAvailableSongs = $entries.length;
$entries.each(function (idx, entry) {
var title_artist_pair = $(entry).find('title')[0].innerHTML;
var title = $.trim(title_artist_pair.split(' - ')[0]);
var artist = $.trim(title_artist_pair.split(' - ')[1]);
var rank = idx + 1;
var query = title + " " + artist;
_searchYouTube(title, artist, rank, query, numAvailableSongs);
});
return;
});
function _searchYouTube(title, artist, rank, query, numAvailableSongs) {
var songs = playlist.get('songs');
$.ajax({
url: searchUrl,
type: "GET",
data: 'q=' + encodeURIComponent(query),
success: function (result) {
//console.log("\n**** 검색: "+query);
ajaxCount += 1;
if (result.items.length)
var videoId = result.items[0].id.videoId;
else
var videoId = null; // Cannot find the song on YouTube
var song = new Song({
title: title,
artist: artist,
rank: parseInt(rank),
query: query,
videoId: videoId
});
// Insert songs into the playlist in the order of their ranks
// *Note: Songs that do not exist on YouTube are ignored
if (videoId) songs.add(song, { silent: true });
// All the ajax calls are finished
if (ajaxCount == numAvailableSongs) {
var popupWindow = chrome.extension.getViews({ type: "popup" })[0];
if (popupWindow && popupWindow.popupView) popupWindow.popupView.setProgress(70);
songs.comparator = 'rank'
songs.sort();
// Remove useless playlsit methods
if (!playlist.get('currentSong')) {
playlist.set('currentSong', songs.at(0));
}
callback();
}
},
error: function (error) {
ajaxCount += 1;
if (ajaxCount == numAvailableSongs) {
var popupWindow = chrome.extension.getViews({ type: "popup" })[0];
if (popupWindow) popupWindow.popupView.setProgress(70);
if (!playlist.get('currentSong')) {
playlist.set('currentSong', songs.at(0));
}
callback();
}
} // end of error
}); // end of second ajax
} // end of _searchYouTube()
},
lookUpAndAddSingleSong: function (query) {
var playlist = this;
var youtubeAPIKey = "fdf";
var initialSearchURL = "df";
var searchUrl = initialSearchURL + "&key=" + youtubeAPIKey;
$.ajax({
url: searchUrl,
type: "GET",
data: 'q=' + encodeURIComponent(query),
success: function (result) {
if (result.items.length) {
var videoId = result.items[0].id.videoId;
var song = new Song({
title: result.items[0].snippet.title,
query: query,
videoId: videoId
});
} else var videoId = null; // Cannot find the song on YouTube
if (videoId) {
playlist.get('songs').add(song);
song.save(); // save to localStorage
}
}, error: function (xhr, status, errorThrown) {
var errorMessage = "lookUpAndAddSingleSong error: check http://instantmusicapp.com";
var popupWindow = chrome.extension.getViews({ type: "popup" })[0];
if (popupWindow && popupWindow.popupView) popupWindow.showErrorMessage(errorMessage);
return;
}
});
},
setMusicChart: function (chartName) {
// if the chart is provided, we pick from Melon, Billboard, iTunes
if (chartName) {
if (chartName == chrome.i18n.getMessage("melonChart"))
this.set('musicChart', AVAILABLE_CHARTS.melonChart);
else if (chartName == chrome.i18n.getMessage("billboardChart"))
this.set('musicChart', AVAILABLE_CHARTS.billboardChart);
else
this.set('musicChart', AVAILABLE_CHARTS.itunesChart);
// else, the user is looking for a personal favorite chart
} else {
this.set('musicChart', AVAILABLE_CHARTS.myChart);
}
}
});
return Playlist;
});