3

我有一个类别树,我想使用 Backbone.js 进行渲染,而不是使用 jQuery 或在服务器端进行渲染。我有以下突破,我将其描述为模板:

<li>
    <select class="categories">
        <option value="">Select</option>
    </select>
    <input class="edit" type="button" value="Edit">
    <input class="add" type="button" value="Add">
</li>

select我单独渲染为内部视图的标签。当我更改选择时,我需要从服务器获取嵌套类别并li使用上面包含在标签中的模板将它们附加到标签中ul。在外部视图中,我创建内部视图并在编辑和添加时点击监听事件。当嵌套级别超过一个时,我对最后一个事件有疑问,因为它们的触发次数与嵌套级别相同。我究竟做错了什么?

下面的代码片段显示了我如何使用外部和内部视图:

    var CategoriesInnerView = Backbone.View.extend({
        tagName:       'select',
        initialize:    function(){
            _.bindAll(this,'addOne','addAll');
            this.collection.bind('reset',this.addAll);
        },
        addOne:        function(category){
            this.$el.append(new CategoryView({model:category}).render().el);
        },
        addAll:        function(){
            this.collection.each(this.addOne);
        },
        events:        {
            'change':'changeSelected'
        },
        changeSelected:function(){
            var children = new Categories();
            children.url = 'categories/' + this.$el.val();

            var childrenView = new CategoriesOuterView({collection:children});
            this.$el.parent().find('ul').remove();
            this.$el.parent().append(childrenView.render().el);

            children.fetch();
        }
    });

    var CategoriesOuterView = Backbone.View.extend({
        tagName:   'ul',
        template:  _.template($('#categories-template').html()),
        initialize:function(){
            this.inner = new CategoriesInnerView({collection:this.collection});
        },
        render:    function(){
            this.$el.html(this.template);

            this.inner.setElement(this.$('select')).render();

            return this;
        },
        events:    {
            'click .edit':'edit',
            'click .add': 'add'
        },
        edit:    function(){
            this.renderForm(this.collection.get(this.inner.$el.val()));
        },
        add:    function(){
            this.renderForm(new Category());
        },
        renderForm:function(category){
            // some code to render the form
        }
    });
4

2 回答 2

3

当您设置嵌套视图时,您必须考虑到事件会在 DOM 树中冒泡,并且 Backbone 在 vi​​ew.el 级别处理 DOM 事件。这意味着在您的场景中,如果您让事件在层次结构中上升,嵌套节点也将触发父节点中的事件。

有关演示,请参见http://jsfiddle.net/PX2PL/

一个简单的解决方案是在回调中停止事件传播

var CategoriesOuterView = Backbone.View.extend({
    events:    {
        'click .edit':'edit',
        'click .add': 'add'
    },
    edit: function(e) {
        e.stopPropagation();
        this.renderForm(this.collection.get(this.inner.$el.val()));
    },
    add: function(e) {
        e.stopPropagation();
        this.renderForm(new Category());
    }
}

还有一个更新的演示http://jsfiddle.net/PX2PL/1/

于 2012-12-29T12:28:22.510 回答
2

我们确实需要查看您的一些代码才能正确回答这个问题,但听起来好像正在发生两件事之一:

1)您在同一元素上多次实例化您的视图

2)您的事件选择器太宽泛

但是如果没有真正看到(相关部分)你的观点,很难说更多。


切向相关边注

顺便说一句,当你有这样的事情时,你可以采取两种基本的方法:

1)您可以让您的父视图创建子视图,并将事件处理放在子视图上

2)您可以让您的父视图创建子视图,也可以不创建(它可以创建所有 HTML 本身),并将事件处理放在上面。

#1 的优点是简单:您的事件处理程序只需引用即可this引用相关视图。但是,如果您必须将 #1 放大太多,则会出现问题:数百万个视图,每个视图都有自己的事件处理程序,会损害性能。

所以,如果性能是/将是重要的,那么#2 会更好,因为你只做一组事件连接。但是,您的事件处理程序必须更聪明,因为他们必须根据提供的事件(即e.target,与this#1 方法中的比较)确定他们正在处理的元素。

于 2012-12-28T21:05:53.027 回答