2013-01-20 89 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(); 
}