BackboneJS:视图渲染正常,但使用未定义的集合进行刷新

3
我已经研究了一些Backbone相关的教程并创建了一些简单的应用程序。现在我再次从头开始,试图使用Backbone提供的好功能。 但是我的视图有问题。当页面加载时,它可以正常渲染并通过迭代集合来创建其嵌套视图。但是,当我再次调用render()来刷新整个列表或者只是单个条目时,所有视图属性似乎都未定义。
单个条目的模型:
Entry = Backbone.Model.extend({
});

一份条目清单:(json.html 是数据来源的占位符)

EntryCollection = Backbone.Collection.extend({
    model: Entry, 
    url: 'json.html'
});

var entries = new EntryCollection();

此视图用于单个条目,它会填充 Underscore 模板,并在模型更改时重新渲染自身。

EntryView = Backbone.View.extend({
    template: _.template($('#entry-template').html()), 
    initialize: function(){
        this.model.on('change', this.render);
    },
    render: function(){
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
});

这是一个查看整个条目列表的视图,其中针对集合中的每个项目渲染一个EntryView,并且如果添加了新项目,则应该重新呈现自身。该按钮仅用于测试目的。

EntryListView = Backbone.View.extend({
    tagName: 'div',
    collection: entries, 
    events: {
        'click button': 'addEntry'
    },
    initialize: function(){
        this.collection.on('add',this.render);
    },
    render: function(){
        this.$el.append('<button>New</button>');  //to test what happens when a new item is added
        var els = [];
        this.collection.each(function(item){
            els.push(new EntryView({model:item}).render().el);
        });
        this.$el.append(els);
        $('#entries').html(this.el);
        return this;
    },
    addEntry: function(){
        entries.add(new Entry({
            title: "New entry", 
            text: "This entry was inserted after the view was rendered"
        }));
    }
});

现在,如果我从服务器获取集合,视图会正常渲染:
entries.fetch({
    success: function(model,response){
        new EntryListView().render();
    }
});

当我点击添加项目到集合的按钮时,EntryListView上的事件处理程序就会捕获'add'事件并调用render()。但是,如果我在render函数中设置断点,我会发现所有属性似乎都是"未定义的"。没有el,也没有collection...我错在哪里了? 谢谢您的帮助, 罗伯特
1个回答

4

目前,EntryListView.render没有绑定到特定的上下文,这意味着作用域(this)由调用者设置:当您单击按钮时,它将设置为您的集合。

您有多种选择来解决问题:

  1. specify the correct context as third argument when applying on

    initialize: function(){
        this.collection.on('add', this.render, this);
    },
    
  2. bind your render function to your view with _.bindAll:

    initialize: function(){
        _.bindAll(this, 'render');
        this.collection.on('add', this.render);
    },
    
  3. use listenTo to give your function the correct context when called

    initialize: function(){
        this.listenTo(this.collection, 'add', this.render);
    },
    

通常情况下,您会执行2或3个步骤,_.bindAll可以确保上下文正确,listenTo在销毁视图时具有额外的好处。

initialize: function(){
    _.bindAll(this, 'render');
    this.listenTo(this.collection, 'add', this.render);
},

如果我可以提一点建议:

  • 不要在 fetch 回调中创建主视图,将其引用到某个地方以便稍后进行操作
  • 不要在视图原型中声明收集器/模型,应该将它们作为参数传递
  • 不要在视图中硬编码 DOM 元素,应该将它们作为参数传递

例如:

var EntryListView = Backbone.View.extend({
    events: {
        'click button': 'addEntry'
    },
    initialize: function(){
        _.bindAll(this, 'render');
        this.listenTo(this.collection, 'reset', this.render);
        this.listenTo(this.collection, 'add', this.render);
    },
    render: function(){
        var els = [];
        this.collection.each(function(item){
            els.push(new EntryView({model:item}).render().el);
        });

        this.$el.empty();
        this.$el.append(els);
        this.$el.append('<button>New</button>');
        return this;
    },
    addEntry: function(){
        entries.add(new Entry({
            title: "New entry", 
            text: "This entry was inserted after the view was rendered"
        }));
    }
});

var view = new EntryListView({
    collection: entries,
    el: '#entries'
});
view.render();

entries.fetch({reset: true});

And a demo http://jsbin.com/opodib/1/edit


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接