0

我正在构建一个小应用程序,用于使用 Backbonejs 从 ul 添加和删除 li。其中一个 SO 成员 cymen 帮助我编写了代码,使用它我稍微定制了代码。目前,如果我添加一个元素并删除,它可以工作,但是第二次我添加一个元素(到 ul)并删除它,我得到

未捕获的类型错误:无法调用未定义的方法“删除”

在这里粘贴我的代码,

HTML:

<input type="text" id="name">
<button id="add">Add</button>
<ul id="mylist"></ul>

JS:

$(function(){

        var myCollection = Backbone.Collection.extend();

        var myView = Backbone.View.extend({

            el:$('body'),

            tagName:'li',

            initialize : function(e){
                this.collection.bind("add",this.render,this);
                this.collection.bind("remove",this.render,this);
            },

            events:{
                'click #add' : 'addfoo'                
            },

            addfoo : function(){
               var myname= $('#name').val();
               $('#name').val('');               
               this.collection.add({name:myname});
            },

            render : function(){

                $('#mylist').empty();
                this.collection.each(function(model){
                    console.log("myView");
                    var remove = new myRemoveView({model:model});
                    remove.render();
                });
            }
        });

        var myRemoveView = Backbone.View.extend({

        el:$('body'),

        events:{
            'click .button':'removeFoo'
        },

        removeFoo : function(){
            console.log("here");

            this.model.collection.remove(this.model);
        },

        render : function(){
            console.log("second view");
            $('#mylist').append('<li>'+this.model.get('name') + "<button class='button'>"+"delete"+"</button></li>");
            return;
        }


        });
        var view = new myView({collection: new myCollection()});
            });

我不明白的两件事:

i) 在 removeFoo 函数中,我们写

this.model.collection.remove(this.model)

这不应该是 this.collection.model.remove 之类的吗?

ii)我向 ul 添加一个 li,然后删除它,当我添加另一个 li 时(附加到 ul 完美)但是这次当我去删除它时会抛出上述错误:Uncaught TypeError:无法调用方法'remove'未定义的

你能帮我弄清楚我的代码中的这两个疑问吗,顺便说一句,SO 成员 cymen 的代码就像一个魅力,只有我定制的代码(上面)给了我错误。

SO 成员 cymen 的代码:JS Fiddle 的代码

谢谢

4

1 回答 1

2

首先,您myRemoveView正在使用<body>el

var myRemoveView = Backbone.View.extend({
    el:$('body'),

这意味着每次你点击删除按钮时,你都会触发你所做removeView的每一个。myRemoveView这当然不是你想要发生的事情,也是 cymen 使用tagName: 'li'. 视图及其els 有两个一般规则:

  1. 每个视图一个视图el和一个el视图。el由于事件处理问题,您不希望视图共享相同的视图;加倍,因此您不希望同一视图的多个实例共享一个el.
  2. 视图el应该是视图关心的 DOM 的一部分,不多也不少。这使得清理变得非常容易,只需删除与您的视图关联的 DOM 元素,大多数事情都会随之消失(尤其是 DOM 事件);对于非 DOM 事件,我们有一个remove视图方法。

另一件事,您的视图(通常)不应该与它自己的 DOM 之外的 DOM 混淆el。这看起来很奇怪:

render : function(){
    $('#mylist').append(...);
    return;
}

调用者应该负责弄清楚你的el去向,你的视图应该只关心它内部发生的事情,其他一些视图应该负责#mylist。此外,render方法通常会返回this,以便您可以执行以下操作:

$(x).append(some_view.render().el);

将它们放入 DOM。

同时指定tagNameel在视图中:

var myView = Backbone.View.extend({
    el: $('body'),
    tagName: 'li',

是没有意义的,tagName将被忽略并被el使用。

您还在小提琴中使用 Backbone 0.5.3。您应该尽可能使用最新版本。

如果我们更正上述内容,那么一切都会开始工作(再次):

var myView = Backbone.View.extend({
    //...
    render: function() {
        $('#mylist').empty();
        this.collection.each(function(model) {
            var remove = new myRemoveView({
                model: model
            });
            $('#mylist').append(remove.render().el);
        });
    }
    //...
});

和:

var myRemoveView = Backbone.View.extend({
    tagName: 'li',
    //...
    render: function() {
        this.$el.text(this.model.get('name'));
        this.$el.append("<button class='button'>delete</button>");
        return this;
    }
});

演示:http: //jsfiddle.net/ambiguous/2z4SA/1/


那么你的原始版本发生了什么样的疯狂?关键el: $('body')在你的myRemoveView. 首先,我们将添加一个小日志记录方法,myRemoveView以便更轻松地观察发生的情况:

_log: function(method) {
    console.log(
        method,
        ': model =', this.model.cid,
        ' got-collection =', this.model.collection ? 'yes' : 'no',
        ' view =', this.cid
    );
}

请注意,这cid是 Backbone 创建的内部唯一 ID,它只是一种方便的方式来跟踪事物。然后我们将this._log调用removeFooand render

removeFoo: function() {
    this._log('removeFoo');
    if(this.model.collection)
        this.model.collection.remove(this.model);
},
render: function() {
    this._log('render');
    $('#mylist').append('<li>' + this.model.get('name') + "<button class='button'>" + "delete" + "</button></li>");
    return;
}

这是一个简单的过程,应该告诉你一切都出错了:

  1. 将a添加到列表中。
  2. b添加到列表中。
  3. 点击a的删除按钮。

你可以跟着这里:http: //jsfiddle.net/ambiguous/yLYNL/

首先,我们将添加一个,这会在控制台中弹出:

render : model = c1  got-collection = yes  view = view2

然后我们添加b并看到:

render : model = c1  got-collection = yes  view = view4
render : model = c3  got-collection = yes  view = view5

您的myView渲染重绘了整个集合,因此我们看到c1ac3新的b。到目前为止一切顺利,一切都说得通。

现在,当我们尝试删除一个; 首先我们看到我们removeFoo一个

removeFoo : model = c1  got-collection = yes  view = view2

这将触发myView#render重绘整个集合的 a。此时整个集合只是b,所以我们c3再次看到渲染,一切仍然有意义:

render : model = c3  got-collection = yes  view = view6

但现在我们看到一切都在横向发展:

removeFoo : model = c1  got-collection = no   view = view4
removeFoo : model = c3  got-collection = yes  view = view5 

您会看到c3出现,因为您有两个myRemoveView实例(一个用于a和一个用于b)绑定到相同el的,因此它们都将看到该click .delete事件,碰巧c1首先看到它。

但那c1在那里做什么?那是没有集合的那个,那是触发原始错误的那个。你永远不会将你myRemoveView的 s 从事件中分离出来,<body>所以你有一个僵尸:即使s<li>消失了,视图仍然<body>通过视图的delegate调用绑定到。所以你有一个僵尸视图,它引用了一个僵尸模型,到处都是僵尸,你把你的僵尸战斗套件留在了车里。但是为什么没有c1收藏呢?好吧,你做到了:

this.model.collection.remove(this.model)

c1将其从收藏中删除;从集合中删除模型会删除模型的collection,因为模型不再在集合中。

于 2012-11-25T05:03:12.607 回答