2012-11-04 70 views
0

我用在我看來,下面的代碼來獲取我的收藏從服務器:骨幹Collection.fetch()返回第一項空

initialize: function() { 
     _this = this; 
     this.collection.fetch({ 
      success : function(collection, response) { 
       _.each(response, function(i){ 
        var todo = new TodosModel({ 
         id: i.id, 
         content: i.content, 
         completed: i.completed 
        }); 
        // Add to collection 
        _this.collection.add(todo); 
        // Render 
        _this.render(todo); 
       }); 
      },  
      error : function(collection, response) { 
       console.log('ERROR GETTING COLLECTION!'); 
      } 
     }); 
    }, 

這似乎工作 - 這裏是從我的服務器的輸出:

{ 
    "0": { 
    "id": 1, 
    "content": "one", 
    "completed": false 
    }, 
    "3": { 
    "id": 4, 
    "content": "two", 
    "completed": true 
    }, 
    "4": { 
    "id": 5, 
    "content": "tester", 
    "completed": false 
    } 
} 

除了事實,如果我登出我的收藏有一個null進入第一位置:

enter image description here

然後導致問題,就像我添加一個項目,它需要最後一個元素的ID。我是新來的骨幹,希望我只是想念一些簡單的東西。

+0

如果你有'_(response).each(function(o){console.log(o)});'?你的'每個人'可能會挑選你不期望的'迴應'。 –

+0

我很想知道,但這是我得到的:http://i.imgur.com/rxqcS.png – Fluidbyte

+0

以下是完整的代碼:http://sandbox.fluidbyte.org/todos/js/todos。 js – Fluidbyte

回答

1

這是我的破解快速運行你的代碼。我沒有測試過任何東西,所以可能會出現拼寫錯誤。我仍然不確定哪裏會出現流浪的空模型,但如果您按照以下所述重構應用程序,我懷疑問題會消失。

模型和集合看起來不錯,讓我們看看你的看法。

el: $('#todos'), 
listBlock: $('#todos-list'), 
newTodoField: $('#add input'), 
//... 
template: $('#todo-template').html(), 
//... 
events: { /* ... */ }, 

這些應該沒問題,但是您需要確保當您的視圖「class」加載時,所有這些元素都在DOM中。通常你會編譯模板一次:

template: _.template($('#todo-template').html()), 

,然後只用this.template作爲一個函數來獲取你的HTML。我將假設template是下面編譯的模板函數。

initialize: function() { 
    _this = this; 

您在這裏有一個意外的全局變量,這可能會導致有趣的錯誤。你想說var _this = this;

this.el = $(this.el); 

骨幹已經給你el$el的jQuery'd版本,所以你不需要這麼做,只是使用this.$el

this.collection.fetch({ 
     success : function(collection, response) { 
      _.each(response, function(i) { 
       var todo = new TodosModel({ /* ... */ }); 
       // Add to collection 
       _this.collection.add(todo); 
       // Render 
       _this.render(todo); 
      }); 
     }, 
     //... 

收集的fetchsuccess處理程序之前的模型添加到集合稱爲所以你不必創建新模型或添加任何東西到集合中。一般來說,render方法渲染整個事物,而不是隻渲染一個部分,並將視圖的render綁定到集合的"reset"事件;該fetch通話將觸發"reset"事件時,它已經提取所以通常的模式是這樣的:

initialize: function() { 
    // So we don't have to worry about the context. Do this before you 
    // use `render` or you'll have reference problems. 
    _.bindAll(this, 'render'); 

    // Trigger a call to render when the collection has some stuff. 
    this.collection.on('reset', this.render); 

    // And go get the stuff we want. You can put your `error` callback in 
    // here if you want it, wanting it is a good idea. 
    this.collection.fetch(); 
} 

現在對於render

render: function (todo) { 
    var templ = _.template(this.template); 
    this.listBlock.append(templ({ 
     id: todo.get('id'), 
     content: todo.get('content'), 
     completed: todo.get('completed') 
    })); 
    // Mark completed 
    if(todo.get('completed')) { 
     this.listBlock.children('li[data-id="'+todo.get('id')+'"]') 
         .addClass('todo-completed'); 
    } 
} 

通常這會被分成兩個部分:

  1. render呈現整個集合。
  2. 另一種方法,如renderOne,呈現單個模型。這也允許您將renderOne綁定到該集合的"add"事件。

因此,像這將是典型的:

render: function() { 
    // Clear it out so that we can start with a clean slate. This may or 
    // may not be what you want depending on the structure of your HTML. 
    // You might want `this.listBlock.empty()` instead. 
    this.$el.empty(); 

    // Punt to `renderOne` for each item. You can use the second argument 
    // to get the right `this` or add `renderOne` to the `_.bindAll` list 
    // up in `initialize`. 
    this.collection.each(this.renderOne, this); 
}, 

renderOne: function(todo) { 
    this.listBlock.append(
     this.template({ 
      todo: todo.toJSON() 
     }) 
    ) 
    // Mark completed 
    if(todo.get('completed')) { 
     this.listBlock.find('li[data-id="' + todo.id + '"]') 
         .addClass('todo-completed'); 
    } 
} 

注意使用toJSON提供數據的模板。骨幹模型和集合有一個toJSON方法來給你一個簡化版本的數據,所以你不妨使用它。該模型的id可作爲屬性使用,因此您不必使用get即可獲取。你可以(並且可能應該)推todo-completed邏輯到模板,只是有點

<% if(completed) { %>class="completed"<% } %> 

在正確的地方應該做的伎倆。

addTodo: function (e) { 
    //... 

     var todo = new TodosModel({ 
      id: todoID, 
      content: todoContent, 
      completed: todoCompleted 
     }); 
     this.render(todo); 
     todo.save(); 
     _this.collection.add(todo); 

你可以綁定renderOne到集合的"add"事件採取渲染新模式的照顧。然後使用save回調來完成它:

var _this = this; 
var todo = new TodosModel({ /* ... */ }); 
todo.save({}, { 
    wait: true, 
    success: function(model, response) { 
     // Let the events deal with rendering... 
     _this.collection.add(model); 
    } 
}); 

同樣,一個error回調的save可能是好的。

completeTodo: function (e) { 
    //... 
    todo.save({ 
     completed: todoCompleted 
    }); 
} 

save來電來訪將掀起'change:completed'事件,這樣你可以綁定到調整HTML。

removeTodo: function (e) { 
    //... 
} 

destroy通話將觸發模型and on the collection一個"destroy"事件:

被觸發集合模型中的任何事件也會 可以在收集直接觸發,以方便使用。這 可以讓你聽一個集合中的變化,以特定的屬性在任何模型 ,[...]

所以,你可以聽上收集"destroy"事件,並使用這些從顯示屏中刪除TODO 。如果沒有您的介入,銷燬模型應該將其從集合中刪除。

printColl: function() { 
    this.collection.each(function (todo) { 
     console.log('ID: '+todo.get('id')+' | CONTENT: '+todo.get('content')+' | COMPLETED: '+todo.get('completed')); 
    }); 
} 

你可以只console.log(this.collection.toJSON())相反, 你必須按一下週圍一點點開拓在 控制檯的東西,但你不會錯過任何東西的方式。

集合的所有事件綁定都將發生在您的 視圖的initialize方法中。如果你要刪除的視圖,然後 你想覆蓋remove從收集 解除綁定,以防止內存泄漏:

remove: function() { 
    // Call this.collection.off(...) to undo all the bindings from 
    // `initialize`. 
    //... 

    // Then do what the default `remove` does. 
    this.$el.remove() 
} 

你也可以使用一個單獨查看每個TODO項目但可能會讓事情變得簡單。

+0

非常感謝您花時間瀏覽本文。我已經開始瀏覽代碼並試圖理解它。我在下劃線上出現錯誤,現在'id沒有定義',並且不能在我的生活中找到它來自哪裏:http://sandbox.fluidbyte.org/todos/ – Fluidbyte

+0

好吧,想通了 - renderOne下的模板設置是錯誤的,應該是'this.template(todo.toJSON())'。現在,它似乎只提取模型默認值(請參閱集合打印到控制檯),而不是渲染新項目。 – Fluidbyte

+1

很抱歉發表評論,非常激動,終於明白這一點。我大部分工作正常,唯一不能工作的是當你添加一個新項目時 - 它增加了集合,但不是DOM。我有沒有想念某個聽衆? – Fluidbyte