我想知道为什么渲染索引函数的尝试给我一个cannot call method render of null
错误。有没有办法使索引函数等待呈现,直到路由器的初始化完成。 console.logs似乎表明索引函数试图在初始化中的ajax调用完成之前呈现
1.Uncaught TypeError: Cannot call method 'render' of null gallery.js:231
2. XHR finished loading: "http://localhost:3000/readjsonfile". jquery.js:8241
3.success
这是代码。我有路线
var Gallery = Backbone.Router.extend({
routes: {
"": "index",
},
初始设置为null
_index: null,
Gallery路由器的初始化检查索引是否为null,如果是,则创建带有数据的新视图。
initialize: function(options) {
var ws = this;
if (this._index === null){
$.ajax({
url: 'galleries',
dataType: 'json',
data: {},
success: function(data) {
console.log("success");
console.log(data);
ws._data = data;
ws._photos = new PhotoCollection(data);
ws._index = new IndexView({model: ws._photos});
console.log(ws._index);
Backbone.history.loadUrl();
},
error: function(r){
console.log("error");
console.log(r);
}
});
return this;
}
return this;
},
这是在初始化之后立即放置的索引函数,它呈现在上面的初始化中创建的视图,但是,我收到Cannot call method 'render' of null
错误
index: function() {
this._index.render();
},
答案 0 :(得分:3)
Backbone.history.start
会触发与当前网址匹配的路由,因此您需要延迟调用它,直到您的路由器准备就绪。
因此,不要在loadUrl
成功回调中调用$.ajax
,而是等待加载依赖项并调用history.start:
success: function(data) {
//...
Backbone.history.start();
}
答案 1 :(得分:1)
默认情况下,XHR请求是异步的,这意味着您的$.ajax
调用不会阻止,并且您的初始路由将立即匹配,此时尚未定义_index
并且您收到错误
如果您的代码如下所示,
var r = new Gallery();
Backbone.history.start();
会发生什么
router.initialize
被称为router.initialize
结束Backbone.history.start
触发索引路由router.index
被称为检查此小提琴以重现您的问题http://jsfiddle.net/nikoshr/krFtA/
你可以做的一些事情:
如果可能,bootstrap models in your page load,这将为您节省请求和一些麻烦,
强制使用async: false
的同步请求阻止,直到您的数据可用为止(基本上,我认为您的想法):
$.ajax({
async: false,
url: 'galleries',
dataType: 'json',
data: {},
success: function(data) {
...
},
error: function(r){
...
}
})
正如@fencliff所说,延迟Backbone.history.start
直到你准备好。
或使用jQuery> = 1.5,您可以使用deferred object来同步您的请求和渲染
var PhotoCollection = Backbone.Collection.extend({
url: 'galleries',
initialize: function() {
this.loaded = $.Deferred();
},
fetch: function(opts) {
var ld = this.loaded,
xhr = Backbone.Collection.prototype.fetch.call(this, opts);
xhr.always(ld.resolve);
}
});
var IndexView = Backbone.View.extend({
el: '#v',
initialize: function() {
_.bindAll(this);
},
render: function() {
this.$el.html('rendered');
console.log('render');
}
});
var Gallery = Backbone.Router.extend({
routes: {
"": "index"
},
_index: null,
initialize: function(options) {
this._photos = new PhotoCollection();
this._photos.fetch();
this._index = new IndexView({model: this._photos});
console.log(this._index);
},
index: function() {
var view = this._index;
view.model.loaded.always(view.render);
}
});