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