3

几个星期以来,我一直在玩 Backbone,并根据教程制作了一些简单的应用程序。现在我再次从头开始,并尝试使用 Backbone 提供的不错的功能,就像我应该做的那样。

不过,我的观点妨碍了我。当页面加载时,它会很好地呈现并通过迭代集合来创建其嵌套视图。当我再次调用 render() 以刷新单个条目的整个列表时,所有视图属性似乎都未定义。

单项型号:

Entry = Backbone.Model.extend({
});

条目列表:(json.html 是数据端的占位符)

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

var entries = new EntryCollection();

查看单个条目,该条目填充下划线模板并应在模型更改时重新呈现自身。

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

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

罗伯特

4

1 回答 1

4

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

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

  1. 应用时指定正确的上下文作为第三个参数on

    initialize: function(){
        this.collection.on('add', this.render, this);
    },
    
  2. 使用_.bindAllrender将您的函数绑定到您的视图:

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

    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});

还有一个演示http://jsbin.com/opodib/1/edit

于 2013-07-03T12:14:57.927 回答