背景:
界面有两个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);
};
?
?
?
?
?
?