backbonejs删除异常概念

时间:2012-11-24 14:19:06

标签: jquery backbone.js backbone-events

我正在构建一个小应用程序,用于添加和删除来自ul的li使用Backbonejs.One的SO成员cymen帮我编码它,使用我定制代码一点点。目前如果我添加一个元素并删除,它的工作原理,但第二次我添加一个元素(到ul)并去删除它,我得到

  

未捕获的TypeError:无法调用未定义的方法'remove'

在此处粘贴我的代码,

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)我添加一个li到ul,然后我删除它,当我添加另一个li(附加到ul工作完美)但这次当我去删除它抛出我上面的错误:未捕获TypeError:无法调用方法'删除'未定义

你可以帮我解决一下我的代码中的这两个疑问吗,顺便说一句SO成员的cymen代码就像一个魅力只有我的定制代码(上图)给我错误。

SO成员cymen的代码:JS Fiddle for his code

谢谢

1 个答案:

答案 0 :(得分:2)

首先,您的myRemoveView正在使用<body>作为其el

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

这意味着每次点击删除按钮,您都会在每个removeView上触发myRemoveView。这肯定不是你想要发生的事情,也不是cymen使用tagName: 'li'的部分原因。视图有两个通用规则及其el

  1. 每个el一个视图,每个视图一个el。由于事件处理问题,您不希望视图共享相同的el;加倍,所以你不希望同一个视图的多个实例共享一个el
  2. 视图的el应该是视图所关注的DOM的一部分,不多也不少。这使得清理变得非常简单,只需删除与您的视图关联的DOM元素,并且大多数事情都会消失(特别是DOM事件);对于非DOM事件,我们在视图上有remove方法。
  3. 另一方面,你的观点不应该(通常)在它自己的el之外搞乱DOM。这看起来很奇怪:

    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._logremoveFoo中致电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 的删除按钮。
    4. 您可以点击这里:http://jsfiddle.net/ambiguous/yLYNL/

      首先我们将添加 a ,然后在控制台中弹出:

      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渲染会重新绘制整个集合,因此我们会看到c1代表 a c3代表新 b 。到目前为止,一切都很有意义。

      现在,当我们尝试删除 a ;首先,我们在 a

      上看到我们removeFoo
      removeFoo : model = c1  got-collection = yes  view = view2
      

      这会触发一个myView#render重绘整个集合。此时整个集合只是 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个实例(一个用于一个,一个用于 b )绑定到相同el,因此他们都会看到click .delete事件,恰好c1首先看到它。

      c1在那里做什么呢?那是没有集合的那个,就是那个触发原始错误的集合。您永远不会将myRemoveView<body>上的活动分开,因此您拥有一个僵尸:即使<li>消失,视图仍然会通过视图{<body>绑定到c1 {3}}致电。所以你有一个僵尸视图,引用僵尸模型,僵尸僵尸无处不在,你把你的僵尸格斗工具包放在车里。但为什么this.model.collection.remove(this.model) 没有收藏?好吧,你做了:

      c1
      collection

      将其从集合中删除;从集合中删除模型会删除模型的{{1}},因为模型不再存在于集合中。