Collections Backbone Collection.fetch()返回第一项null

Collections Backbone Collection.fetch()返回第一项null,collections,backbone.js,Collections,Backbone.js,我在视图中使用以下代码从服务器获取我的收藏: initialize: function () { _this = this; this.collection.fetch({ success : function(collection, response) { _.each(response, function(i){ var todo = new TodosMode

我在视图中使用以下代码从服务器获取我的收藏:

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
条目:


这会导致问题,就好像我添加了一个项目,它采用最后一个元素的ID一样。我是主干网新手,希望我只是错过了一些简单的东西。

下面是我对您的代码的一个快速浏览。我没有测试过任何东西,所以可能会有打字错误。我仍然不确定这个零散的空模型是从哪里来的,但是如果你按照下面概述的方式重新构造你的应用程序,我怀疑这个问题会消失

模型和系列看起来不错,让我们看看您的视图

el: $('#todos'),
listBlock: $('#todos-list'),
newTodoField: $('#add input'),
//...
template: $('#todo-template').html(),
//...
events: { /* ... */ },
这些应该可以,但您需要确保在加载视图“类”时,所有这些元素都在DOM中。通常,您只需编译一次模板:

template: _.template($('#todo-template').html()),
然后使用
this.template
作为获取HTML的函数。我将假定
template
是下面的编译模板函数

initialize: function () {
    _this = this;
这里有一个意外的全局变量,这可能会导致有趣的bug。你想说
var\u this=this

    this.el = $(this.el);
主干已经在中为您提供了一个jQuery版本的
el
,因此您不需要这样做,只需使用
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);
            });
        },
        //...
集合的将在调用
success
处理程序之前将模型添加到集合中,这样您就不必创建新模型或向集合中添加任何内容。通常,
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: 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');
    }
}
通常情况下,这将分为两部分:

  • render
    以渲染整个集合
  • 另一种方法是渲染单个模型,例如
    renderOne
    。这还允许您将
    renderOne
    绑定到集合的
    “添加”
    事件
  • 这样的事情很典型:

    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
    方法来为您提供数据的简化版本,因此您也可以使用它。模型的属性可用,因此您不必使用
    get
    来获取它。您可以(也可能应该)将
    todo completed
    逻辑推送到模板中,只需一点点

    <% if(completed) { %>class="completed"<% } %>
    
    您可以将
    renderOne
    绑定到集合的
    “add”
    事件,以处理新模型的渲染。然后使用回调完成它:

    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);
        }
    });
    
    同样,在
    保存
    上执行
    错误
    回调可能很好

    completeTodo: function (e) {
        //...
        todo.save({
            completed: todoCompleted
        });
    }
    
    此处的
    save
    调用将触发
    'change:completed'
    事件,以便您可以绑定到该事件以调整HTML

    removeTodo: function (e) {
        //...
    }
    
    该调用将在模型上触发一个
    “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())
    即可, 你必须点击一点来打开里面的东西 但这样你就不会错过任何东西

    集合的所有事件绑定都将在 视图的
    初始化
    方法。如果要删除视图,则
    您可能希望覆盖以从集合中解除绑定
    要防止内存泄漏,请执行以下操作:

    remove: function() {
        // Call this.collection.off(...) to undo all the bindings from
        // `initialize`.
        //...
    
        // Then do what the default `remove` does.
        this.$el.remove()
    }
    

    您也可以为每个TODO项目使用单独的视图,但对于一些简单的项目来说,这可能过于复杂了。

    如果您
    \uuu0(响应).each(函数(o){console.log(o)})?您的
    每一个
    都可能在
    响应
    中拾取了您不期望的内容。我想知道,但我得到的是:这是完整的代码:我更新了我的模型上的
    默认值
    ,以包括
    'content':'something'
    ,现在集合中的第一项是
    ID:null | content:something | COMPLETED:false
    。我如何阻止它添加默认值作为集合的成员?你的应用程序在很多方面都很奇怪。例如,集合应该在调用
    success
    处理程序之前为您添加模型,这样您就不必手动完成。如果你愿意,我可以在几个小时内给你一个完整的评论;我怀疑解决这个奇怪的问题会让问题消失。非常感谢你花时间来解决这个问题。我已经开始阅读代码并试图理解它。我在下划线上遇到了一个错误,因为
    id没有定义
    ,我一辈子都找不到它的来源:好的,我想出来了-renderOne下的模板设置是错误的,应该是
    this.template(todo.toJSON())
    。现在,它似乎只获取模型默认值(请参阅“打印到控制台”集合),而不呈现新项目。对于评论流,我很抱歉,很高兴终于理解了这一点。我已经完成了大部分工作,唯一无法完成的事情是当你添加一个新项目时——它会添加到集合中,但不会添加到DOM中。我在什么地方错过了一个听众吗?不用担心这些评论,我只需要出去一下。下划线模板中的JavaScript错误可能是一个真正需要解决的问题,众所周知的黑光在黑色背景上亮起黑色
    remove: function() {
        // Call this.collection.off(...) to undo all the bindings from
        // `initialize`.
        //...
    
        // Then do what the default `remove` does.
        this.$el.remove()
    }