收听事件的骨干视图多次触发

时间:2014-03-16 22:04:27

标签: backbone.js

我在子视图上使用ListenTo时遇到了一个非常奇怪的问题。

基本上我有一个包含多个子视图的主视图。 这些子视图在初始化父视图时也会初始化。 其中一些子视图正在侦听同一个全局集合。

其中一个视图是允许我在集合中插入新条目的表单 第二个是包含在集合中的所有条目的列表。

所以,基本上它看起来像这个

$('button').on('click'.... 
   app.views.MyMainView = new MyMainView()
   //launch new modal window
   //with html body = app.views.MyMainView.el

var MyMainView = Backbone.view.extend({
     initialize: function(){
          // new Form_View()
          // new List_View()
     }
});

var Form_View = Backbone.view.extend({
      //get input value
      // create new entrie into the collection
});

var List_View = Backbone.view.extend({
     initialize: function(){
          // listenTo(this.collection, 'add', this.addOne)
          // new List_View()
          this.addAll();
     },
     addAll: function(){...},
     addOne: function(model){ .... }
});

问题如下: 当用户第一次启动模态时,我们向集合中添加一个新条目 在List_View上的listenTo添加按预期触发。

如果我关闭模态并且用户单击“按钮”以再次启动模态窗口,如果我添加新的entrie,则视图将触发2次正在侦听集合添加事件的函数。

如果我再次关闭模态并重新打开它,该功能将触发3次,依此类推。

这很奇怪,因为每次用户点击“按钮”时,我都会创建一个新的视图实例及其子视图。这就是为什么它对我没有意义。

任何帮助?

修改     我还通过听取其中的“添加”事件来检查我的收藏。

var MyCollection = Backbone.View.extend({
    initialize: function(){
      this.listenTo( this, 'add', doSomething );
    },
    doSomething: function( model ){
        //do something fires as it should be firing the event: 1 time per each item inserted
    }
})

编辑2

var MyMainView = Backbone.View.extend({
.....    
close_modal: function(e){

                if(e){
                    e.preventDefault();
                }

                var viewsLen = this.views.length,
                    _that = this;

                _.each(this.views, function(view, key){

                    view.remove();

                    if(key + 1 == viewsLen )
                        _that.dialog.close();
                })

            }
...
})

编辑3: 所有代码

//初始化模态 $('button')。on('click',function(){      app.views.storePayment_View = new StorePayment_View(); })

var TMPL_StorePayment = '<div class="store-payment">'
                                + '<div class="store-payment-header">'
                                    + '<div class="client"></div>'
                                    + '<div class="status"></div>'
                                + '</div>'
                                + '<div class="payment-form"></div>'
                                + '<div class="payment-list"></div>'
                                + '<div class="x-form-actions">'
                                + '</div>'
                            + '</div>';


    var StorePayment_View = Backbone.View.extend({
        views: {},
        wrappers: {},
        collections: {},
        events: {
            "click .back": "close_modal",
            "click .finish-payment": "finish_payment"
        },
        initialize: function(){
            var _that = this;
            this.dialog = new BootstrapDialog({
                title: appLang["h67"],
                message: this.$el,
                closable: true,
                onhide: function(dialogRef){
                    _that.remove();
                }
            });

            this.dialog.realize();
            this.dialog.getModalFooter().hide();
            this.dialog.open();


            this.$el.html('').append( TMPL_StorePayment );
            this.wrappers.$client = this.$el.find('.client');
            this.wrappers.$status = this.$el.find('.status');
            this.wrappers.$payment_form = this.$el.find('.payment-form');
            this.wrappers.$payment_list = this.$el.find('.payment-list');
            this.wrappers.$form_actions = this.$el.find('.x-form-actions');
            this.render()
        },
        render: function(){



            this.views.StorePaymentForm_View = new StorePaymentForm_View();
            this.wrappers.$payment_form.html('').append( this.views.StorePaymentForm_View.el  );

            this.views.StorePaymentList_View = new StorePaymentList_View();
            this.wrappers.$payment_list.html('').append( this.views.StorePaymentList_View.el  );



        },
        close_modal: function(e){

            if(e){
                e.preventDefault();
            }

            var viewsLen = this.views.length,
                _that = this;

            _.each(this.views, function(view, key){

                view.remove();

                if(key + 1 == viewsLen ){
                    _that.dialog.close();
                }

            })

        }

    })



    var StorePaymentForm_View = Backbone.View.extend({
        error_tmpl: _.template('<div class="alert alert-warning"><%= message %></div>') ,
        template: _.template('<div> <div class="input-field"> <input type="text" class="montant form-control" value="<%= restant %>"> </div> <div class="input-select"> <select name="payment-type"><% _.each(paymentTypeList, function(paymentType){ %>  <option value="<%= paymentType.typeMode %>"><%= paymentType.libelle %></option> <% }) %></select> </div> <div class="actions"><a href="#" class="add-new">Add new</a><a href="#" class="remove-all">Remove All</a> </div></div><div class="error_placeholder"></div>'),
        events:{
            "click .add-new": "add_new",
            "click .remove-all": "remove_all"
        },
        initialize: function(){
            this.collection = app.collections.StorePaymentList;

            this.listenTo( this.collection, 'add', this.render )
            this.listenTo( this.collection, 'destroy', this.render )
            this.listenTo( this.collection, 'change', this.render )
            this.listenTo( this.collection, 'reset', this.render )
            this.render()
        },
        render: function(){
            console.log("RENDER FIRED ON STOREPAYMENTFORM")
            var restant = this.collection.getRestant();
            if ( restant <= 0){
                restant = 0;
            }
            this.$el.html('').append( this.template( { "restant" : restant, "paymentTypeList": app.collections.PaymentTypeList.toJSON() } ) )

            var _that = this;
            setTimeout(function(){
                _that.$el.find('select').selectBoxIt({
                    native: true,
                    autoWidth: false
                })

                _that.$el.find('input').focus();
            }, 50 )
        },
        add_new: function(e){

            console.log("add_new");

            if(e){
                e.preventDefault();
            }

            var _that = this,
                input_val = this.$el.find('input').val(),
                select_val = this.$el.find('select :selected').val(),
                libelle = this.$el.find('select :selected').text(),
                wasNaN = false;


                input_val = parseInt(input_val);
                if (isNaN(input_val)){
                    wasNaN = true;
                    input_val = 0;
                }

                if (wasNaN){
                    _that.$el.find('.error_placeholder').html( _that.error_tmpl( { "message": appLang["h69"] } ) );     
                } else {
                    if ( input_val <= 0 ){
                        _that.$el.find('.error_placeholder').html( _that.error_tmpl( { "message": appLang["h70"] } ) );     
                    } else {
                        this.collection.add( new StorePaymentModel( { "libelle": libelle , "paymentId": select_val, "montant": input_val  } ) ) 
                    }   
                }
        },
        remove_all: function(e){
            if(e){
                e.preventDefault();
            }
            var _that = this;
            //dialog are you sure?
            var dialog = new BootstrapDialog({
                title: "Do you want to continue",
                message: "Do you really want to empty your current list of payments?",
                buttons: [{
                        label: appLang["a187"], //cancel
                        action: function(dialog) {

                            dialog.close();
                        }
                    }, {
                        label: appLang["a1621"], //ok
                        cssClass: 'btn-primary',
                        action: function(dialog) {
                            _that.collection.reset([]);
                            dialog.close();
                        }
                }]
            })

            dialog.realize();
            dialog.open();
        }
    })


    var StorePaymentListItem_View = Backbone.View.extend({
        events:{
            "click .remove": "remove_item",
            "click .save": "save"
        },
        template: _.template( '<%= libelle %> <%= montant %>  <a href="#" class="remove pull-right"><i class="fa fa-trash-o"></i></a>' ),
        tagName: 'li',
        className: 'list-group-item',
        initialize: function(){
            this.render()
            //console.log("StorePaymentListItem_View initialized")
            this.listenTo( this.model, 'hide', this.remove )
        },
        render: function(){

            this.$el.html('').append( this.template( this.model.toJSON() ) )

        },
        edit: function(){
            this.render_edit();
        },
        save: function(e){
            if (e){
                e.preventDefault(); e.stopPropagation();
            }
            this.render();
        },
        remove_item: function(e){
            if (e){
                e.preventDefault(); e.stopPropagation();
            }

            this.model.destroy();
        }
    })

    var StorePaymentList_View = Backbone.View.extend({
        $wrapper: $('<ul />', {'class': 'list-group' }),
        initialize: function(){
            this.$el.html('');
            this.collection = app.collections.StorePaymentList;

            this.listenTo( this.collection , 'add', this.addOne );
            this.listenTo( this.collection , 'change', this.render );
            this.listenTo( this.collection , 'reset', this.render );
            this.render()

        },
        render: function(){
            var totalItems = this.collection.length;
                this.$wrapper.html('')

            if (totalItems == 0){
                this.appendToRoot();
            } else {
                this.addAll()
            }    
        },
        addAll:function(){
            var _that = this,
                totalItems = this.collection.length;

            this.collection.forEach(function(model, key){
                _that.addOne(model)

                if (totalItems == key + 1)
                    _that.appendToRoot();
            })  
        },
        addOne:function( model ){

            var storePaymentListItem_View = new  StorePaymentListItem_View({ model: model });

            this.$wrapper.append( storePaymentListItem_View.el );

        },
        appendToRoot:function(){
            this.$el.html('').append( this.$wrapper );
        }
    })

1 个答案:

答案 0 :(得分:1)

我的猜测是关闭模式并不会调用view.remove,因此我们最终得到一个僵尸视图,该视图从DOM中移除但仍然存在于内存中侦听事件。

另一个猜测是你永远不会删除List_View实例,所以它们就是僵尸。

换句话说,这可能与垃圾收集不足有关。

这是猜测 - 如果没有看到代码的相关部分,就无法判断。