BackboneJS:View渲染很好,但使用未定义的集合刷新

时间:2013-07-03 11:10:16

标签: backbone.js

我几周就在乱用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上的事件处理程序就会捕获&#39;添加&#39;事件和调用render()。但是如果我在渲染函数中设置断点,我可以看到所有属性似乎都是&#34;未定义&#34;。没有el,没有collection ...

我哪里错了? 感谢您的协助,

罗伯特

1 个答案:

答案 0 :(得分:4)

原样,EntryListView.render没有绑定到特定的上下文,这意味着范围(this)由调用者设置:当您单击按钮时,这将设置为您的集合

您有多种方法可以解决您的问题:

  1. 在应用on

    时将正确的上下文指定为第三个参数
    initialize: function(){
        this.collection.on('add', this.render, this);
    },
    
  2. 使用_.bindAll

    render功能绑定到您的视图
    initialize: function(){
        _.bindAll(this, 'render');
        this.collection.on('add', this.render);
    },
    
  3. 使用listenTo在调用时为函数提供正确的上下文

    initialize: function(){
        this.listenTo(this.collection, 'add', this.render);
    },
    
  4. 您通常会执行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});
    

    演示http://jsbin.com/opodib/1/edit

相关问题