2013-01-20 47 views
3

我想知道为什么尝试呈现索引函数给我一个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", 

    }, 

据initally设置为null

_index: 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(); 
    }, 

回答

1

XHR请求默认情况下是异步的,这意味着你的电话$.ajax赢得” t块和您的初始路线将立即匹配,_index尚未定义在那一点,你会得到一个错误。

如果你的代码看起来像这样,

var r = new Gallery(); 
Backbone.history.start(); 

发生的事情是

  1. router.initialize
  2. 一个XHR请求发出
  3. router.initialize结束
  4. Backbone.history.start触发索引路线
  5. router.index
  6. 的XHR请求结束

检查这个小提琴重现您的问题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){ 
         ... 
        } 
    }) 
    

    http://jsfiddle.net/nikoshr/m3dW5/

  • 为@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); 
        } 
    }); 
    

    http://jsfiddle.net/nikoshr/m3dW5/1/

+0

这是一个真棒答案。我会去玩弄它,以确保我能够把它运用起来并理解这一切,然后再回来接受。谢谢。 – BrainLikeADullPencil

3

Backbone.history.start触发路由匹配当前的URL,所以你需要延迟调用它,直到你的路由器准备就绪。的

因此,而不是在$.ajax成功回调调用loadUrl,等到你的依赖被加载并改为调用history.start:

success: function(data) { 
    //... 
    Backbone.history.start(); 
}