背景:
界面有两个GridPanel,分别为发票列表、费用列表。一个必要对应多个费用进行核销。
应用上,选择一个发票记录时,自动加载对应的同一业务下的费用,并计算出总费用金额。将费用总金额回写到选择的发票核销金额字段。
当发票有多行记录时,一切正常。当发票只有一条时,发生死循环。
调试:初步调试发现,如果只有一条发票时,费用加载完自己又清除掉,然后再加载。
查看Ext源代码,发现为Ext2.0的一个bug。在Ext2.2以后都已经解决。
过程:
1,回写发票的核销金额时,调用了Record的set方法。
set : function(name, value){ if(String(this.data[name]) == String(value)){ return; } this.dirty = true; if(!this.modified){ this.modified = {}; } if(typeof this.modified[name] == 'undefined'){ this.modified[name] = this.data[name]; } this.data[name] = value; if(!this.editing && this.store){ this.store.afterEdit(this); } }
2,Store的afterEdit方法,触发update事件
// private afterEdit : function(record){ if(this.modified.indexOf(record) == -1){ this.modified.push(record); } this.fireEvent("update", this, record, Ext.data.Record.EDIT);//触发事件,查看相关的监听者,猜测在GridView或GridPanel },
3,GridView监听了store的update事件,进而触发了onUpdate方法。
而onUpdate直接调用了refreshRow方法。
这里,可以看到当Record改变时,GridView采取的方法是先insertRows再onRemove。最后才触发rowupdated事件。跟踪过rowupdated事件的监听者,未发现异常。
因为是只有一行记录才出现问题,所以需要对数字的判断敏感一些。
当只有一条Record时,在insertRows方法中,实际只是刷新了一下。并没有真正的删除。
接下去就是看refresh方法做什么?
// private
initData : function(ds, cm){
if(this.ds){
//省略……
}
if(ds){
ds.on("load", this.onLoad, this);
ds.on("datachanged", this.onDataChange, this);
ds.on("add", this.onAdd, this);
ds.on("remove", this.onRemove, this);
ds.on("update", this.onUpdate, this);
ds.on("clear", this.onClear, this);
}
this.ds = ds;
//省略....
},
// private
onUpdate : function(ds, record){
this.refreshRow(record);
},
// private
refreshRow : function(record){
var ds = this.ds, index;
if(typeof record == 'number'){
index = record;
record = ds.getAt(index);
}else{
index = ds.indexOf(record);
}
var cls = [];
this.insertRows(ds, index, index, true);
this.getRow(index).rowIndex = index;
this.onRemove(ds, record, index+1, true);
this.fireEvent("rowupdated", this, index, record);
},
insertRows : function(dm, firstRow, lastRow, isUpdate){
if(firstRow === 0 && lastRow == dm.getCount()-1){
this.refresh();
}else{
if(!isUpdate){
this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
}
var html = this.renderRows(firstRow, lastRow);
var before = this.getRow(firstRow);
if(before){
Ext.DomHelper.insertHtml('beforeBegin', before, html);
}else{
Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html);
}
if(!isUpdate){
this.fireEvent("rowsinserted", this, firstRow, lastRow);
this.processRows(firstRow);
}
}
}
4,GridView的refresh触发了refresh事件,进而触发Ext.grid.RowSelectionModel的onRefresh函数。
直接便抛出了选择修改事件。
onRefresh : function(){ var ds = this.grid.store, index; var s = this.getSelections(); this.clearSelections(true); for(var i = 0, len = s.length; i < len; i++){ var r = s[i]; if((index = ds.indexOfId(r.id)) != -1){ this.selectRow(index, true); } } if(s.length != this.selections.getCount()){ this.fireEvent("selectionchange", this); } }
综合上代码跟踪。第3步中的GridView的insertRows方法调用了refresh()是导致问题所在。则重写此方法解决此问题。
/** * 解决Grid只有一条数据时,对该记录调用set方法时,会造成刷新/选择事件 *20101125.1 By Simon */ Ext.grid.GridView.prototype.insertRows = function(dm, firstRow, lastRow, isUpdate) { if (!isUpdate && firstRow === 0 && lastRow >= dm.getCount() - 1) {//加入判断是否只是update this.refresh(); } else { if (!isUpdate) { this.fireEvent("beforerowsinserted", this, firstRow, lastRow); } var html = this.renderRows(firstRow, lastRow); var before = this.getRow(firstRow); if (before) { Ext.DomHelper.insertHtml('beforeBegin', before, html); } else { Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html); } if (!isUpdate) { this.fireEvent("rowsinserted", this, firstRow, lastRow); this.processRows(firstRow); } } this.syncFocusEl(firstRow); };