extjs版本2.1

用extjs的分组store加载数据遇到一个问题,首先store定义如下:

var store = new Ext.data.GroupingStore({
        reader: new BidResultItemXmlReader(),
        url: Ipms.StateAssetOfficeService + '/QueryBidResultItem',
        sortInfo: { field: 'id', direction: "DESC" },
        groupField: 'groupField'
    });

单从字面分析这段代码:从url加载数据,并且按id进行降序排序,然后按groupField字段分组。


但是最终页面展现的数据,却不是这样的,如下图:

extjs GroupingStore sort 的疑问_第1张图片

而后台发送的数据的确是按id降序排列的。


原因就是对Ext排序、分组不够了解,仅仅从字面上进行臆断。

1.Store.js中加载数据的方法loadRecords()源码如下:

loadRecords : function(o, options, success){
    // ...
                                                                                                                                                                                                                                           
    this.applySort(); // 会调用GroupingStore的方法
                                                                                                                                                                                                                                           
    // ..
}


2.GroupingStore.js中applySort()源码:

applySort : function(){
        Ext.data.GroupingStore.superclass.applySort.call(this);
        if(!this.groupOnSort && !this.remoteGroup){
            var gs = this.getGroupState();
            if(gs && gs != this.sortInfo.field){ // 凶手
                this.sortData(this.groupField); // 调用Store的方法
            }
        }
    },

从源码可知,只要我们的排序字段跟分组字段不是相同列的话,他都会再次按分组字段排序。


3.Store.js中sortData()源码:

// private
sortData : function(f, direction){
    direction = direction || 'ASC';
    var st = this.fields.get(f).sortType;
    var fn = function(r1, r2){
        var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
        return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
    };
    this.data.sort(direction, fn);
    if(this.snapshot && this.snapshot != this.data){
        this.snapshot.sort(direction, fn);
    }
},


4. 之所以进行分组之前首先按分组字段进行排序,个人臆断原因有二。

 A. 排序字段跟分组字段不同,即使排好序后,分组还是有可能打破排序的顺序的。

 B. 跟GroupingView的写法有关系,见下文。


5.GroupingView.js的doRender()源码(有删减):

doRender : function(cs, rs, ds, startRow, colCount, stripe){
    var groups = [], curGroup, i, len, gid;
        for(i = 0, len = rs.length; i < len; i++){
            var rowIndex = startRow + i;
            var r = rs[i],
                gvalue = r.data[groupField],
                g = this.getGroup(gvalue, r, rowIndex, colIndex, ds);
            if(!curGroup || curGroup.group != g){ // 关键
                curGroup = {
                    group: g,
                    gvalue: gvalue,
                    groupId: gid,
                    startRow: rowIndex,
                    rs: [r]
                };
                groups.push(curGroup);
            }else{
                curGroup.rs.push(r);
            }
            r._groupId = gid;
        }
}

此方法就是构造分组列表,简单的说,他会循环GroupStore中的每个条目,判断如果groupField的值跟当前组的值相同,则把此条目放入当前分组;否则的话,新建一个分组。


他这么做,就是假设store是按照groupField进行了排序。从applySort()方法中,我们发现他会强制按groupField进行升序排序。这也正是导致最终视图的原因。


6.说些风凉话。在服务端排序在客户端分组,事件很不靠谱的事儿。Ext的处理方式就更不靠谱了(简单看了一下3.2中的处理就优雅多了)。


7. 折中的办法:按照某个字段分组,然后组内的条目按照某某字段分组。所以,干脆把排序和分组要么都放在服务器端,客户端不处理;要么都放在客户端,服务器端不干预。


8.我们采用排序和分组都在服务器端完成,有一定的局限性

var store = new Ext.data.GroupingStore({
        reader: new BidResultItemXmlReader(),
        url: Ipms.StateAssetOfficeService + '/QueryBidResultItem',
        sortInfo: { field: 'id', direction: "DESC" },
        remoteSort: true, // 配置1
        remoteGroup: true,// 配置2
        groupField: 'groupField'
    });