我在子视图上使用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 );
}
})
答案 0 :(得分:1)
我的猜测是关闭模式并不会调用view.remove
,因此我们最终得到一个僵尸视图,该视图从DOM中移除但仍然存在于内存中侦听事件。
另一个猜测是你永远不会删除List_View实例,所以它们就是僵尸。
换句话说,这可能与垃圾收集不足有关。
这是猜测 - 如果没有看到代码的相关部分,就无法判断。