From: PaulE <ut_...@us...> - 2008-09-27 01:01:58
|
Update of /cvsroot/jaffa/JaffaRIA/source/html/js/extjs/ux/grid In directory sc8-pr-cvs17.sourceforge.net:/tmp/cvs-serv32713 Modified Files: MultiGroupingPanel.js MultiGroupingStore.js MultiGroupingView.js Log Message: Updated with new version of MultiGrouping that now supports dynamic live paging. Two examples have been provided, but the live example relies on a JSP back end. Index: MultiGroupingPanel.js =================================================================== RCS file: /cvsroot/jaffa/JaffaRIA/source/html/js/extjs/ux/grid/MultiGroupingPanel.js,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** MultiGroupingPanel.js 29 Aug 2008 22:05:07 -0000 1.2 --- MultiGroupingPanel.js 27 Sep 2008 01:01:00 -0000 1.3 *************** *** 142,143 **** --- 142,231 ---- }); + /** + * @class Ext.ux.grid.MultiGroupingPagingStore + * @extends Ext.ux.grid.MultiGroupingStore + * A specialized {@link Ext.data.Store} that allows data to be appended a page at + * a time as the user scrolls through. It is based on performing server-side sorting + * and grouping and should be used in conjunction with a {@link Ext.ux.grid.MultiGroupPagingGrid} + * @constructor + * Create a new MultiGroupingPagingStore + * @param {Object} config The config object + * + * @author PaulE + */ + Ext.ux.grid.MultiGroupingPagingGrid = Ext.extend(Ext.ux.grid.MultiGroupingPanel, { + + /** When creating the store, register an internal callback for post load processing + */ + constructor: function(config) { + config = config||{}; + config.bbar = [].concat(config.bbar); + config.bbar = config.bbar.concat([ + ' ' + ,'-' + ,{xtype:'tbtext',id:'counter',disabled: false,text: '? of ?'} + ,{xtype:'tbbutton',id:'more',text: 'More...',handler: function() { this.store.loadMore(false); }, scope: this} + ,{xtype:'tbbutton',id:'loading',hidden: true,iconCls: "x-tbar-loading"} + ]); + + Ext.ux.grid.MultiGroupingPagingGrid.superclass.constructor.apply(this, arguments); + // Extend the bottom toolbar, for record paging info + //console.debug("bb=",this.getBottomToolbar()); + var bb=this.getBottomToolbar(); + this.barCounter = bb.items.itemAt(bb.items.length-3); + this.barMore = bb.items.itemAt(bb.items.length-2); + this.barLoading = bb.items.itemAt(bb.items.length-1); + + // As the default onLoad to refocus on the first row has been disabled, + // This has been added so if a load does happen, and its an initial load + // it refocuses. If this is a refresh caused by a sort/group or a new page + // of data being loaded, it does not refocus + this.store.on("load", function(r,o) { + if(o&&o.initial==true) + Ext.ux.grid.MultiGroupingView.superclass.onLoad.call(this); + }, this.view); + + // Create Event that asks for more data when we scroll to the end + this.on("bodyscroll", function() { + var s = this.view.scroller.dom; + if( (s.offsetHeight+s.scrollTop+5 > s.scrollHeight) && !this.isLoading) { + console.debug("Grid.bodyscroll.Event: Get more..."); + this.store.loadMore(false); + } + }, this); + + // When the grid start loading, display a loading icon + this.store.on("beforeload", function(o) { + if(this.isLoading) { + //console.debug("Store.beforeload.Event: Reject Load, on is in progress"); + return false; + } + this.isLoading = true; + this.barLoading.setVisible(true); + console.debug("Store.beforeload.Event: options=",o, this); + return true; + }, this); + + // When loading has finished, disable the loading icon, and update the row count + this.store.on("load", function() { + delete this.isLoading; + this.barLoading.setVisible(false); + //console.debug("Store.load.Event: Finished loading",this.barCounter.getEl()); + this.barCounter.getEl().innerHTML = "Showing " + this.store.getCount()+' of ' + + (this.store.totalCount?this.store.totalCount:'?'); + if(this.store.totalCount) this.barMore.disable(); + return true; + }, this); + + // When a loading error occurs, disable the loading icon and display error + this.store.on("loadexception", function(e) { + console.debug("Store.loadexception.Event:",arguments); + delete this.isLoading; + this.barLoading.setVisible(false); + Ext.Message("Error Loading Records - " + e); + return false; + }, this); + + } + + }); Index: MultiGroupingView.js =================================================================== RCS file: /cvsroot/jaffa/JaffaRIA/source/html/js/extjs/ux/grid/MultiGroupingView.js,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** MultiGroupingView.js 29 Aug 2008 22:05:08 -0000 1.2 --- MultiGroupingView.js 27 Sep 2008 01:01:00 -0000 1.3 *************** *** 20,31 **** this.on("beforerefresh", function() { console.debug("Cleared Row Cache"); ! if(this.rowsCache) delete rowsCache; }, this); } ,displayEmptyFields: false - ,displayFieldSeperator: ', ' - ,renderRows: function(){ //alert('renderRows'); --- 20,30 ---- this.on("beforerefresh", function() { console.debug("Cleared Row Cache"); ! if(this.rowsCache) delete this.rowsCache; }, this); + } ,displayEmptyFields: false ,renderRows: function(){ //alert('renderRows'); *************** *** 35,45 **** if (this.hideGroupedColumn) { var colIndexes = []; ! for (var i = 0, len = groupField.length; i < len; ++i) { ! var cidx=this.cm.findColumnIndex(groupField[i]); ! if(cidx>=0) ! colIndexes.push(cidx); ! else ! console.debug("Ignore unknown column : ",groupField[i]); ! } if (!eg && this.lastGroupField !== undefined) { this.mainBody.update(''); --- 34,45 ---- if (this.hideGroupedColumn) { var colIndexes = []; ! if(eg) ! for (var i = 0, len = groupField.length; i < len; ++i) { ! var cidx=this.cm.findColumnIndex(groupField[i]); ! if(cidx>=0) ! colIndexes.push(cidx); ! else ! console.debug("Ignore unknown column : ",groupField[i]); ! } if (!eg && this.lastGroupField !== undefined) { this.mainBody.update(''); *************** *** 89,93 **** */ ,doRender: function(cs, rs, ds, startRow, colCount, stripe){ ! //console.debug ("doRender: ",cs, rs, ds, startRow, colCount, stripe); var ss = this.grid.getTopToolbar(); if (rs.length < 1) { --- 89,93 ---- */ ,doRender: function(cs, rs, ds, startRow, colCount, stripe){ ! //console.debug ("MultiGroupingView.doRender: ",cs, rs, ds, startRow, colCount, stripe); var ss = this.grid.getTopToolbar(); if (rs.length < 1) { *************** *** 96,102 **** var groupField = this.getGroupField(); ! var gfLen = groupField.length; - // Remove all entries alreay in the toolbar for (var hh = 0; hh < ss.items.length; hh++) { --- 96,101 ---- var groupField = this.getGroupField(); ! var gfLen = groupField?groupField.length:0; // Remove all entries alreay in the toolbar for (var hh = 0; hh < ss.items.length; hh++) { *************** *** 105,115 **** if(gfLen==0) { - // this.cm.setHidden(0,true); ss.addItem(new Ext.Toolbar.TextItem("Drop Columns Here To Group")); ! console.debug("No Groups"); } else { ! // this.cm.setHidden(0,false); ! // this.cm.setColumnWidth(0,gfLen*12); ! console.debug("Set width to",gfLen," Groups"); // Add back all entries to toolbar from GroupField[] --- 104,111 ---- if(gfLen==0) { ss.addItem(new Ext.Toolbar.TextItem("Drop Columns Here To Group")); ! //console.debug("MultiGroupingView.doRender: No Groups"); } else { ! //console.debug("MultiGroupingView.doRender: Set width to",gfLen," Groups"); // Add back all entries to toolbar from GroupField[] *************** *** 124,128 **** b.fieldName = t; ss.addItem(b); ! console.debug("Added Group to Toolbar :",this,t,'=',b.text); } } --- 120,124 ---- b.fieldName = t; ss.addItem(b); ! //console.debug("MultiGroupingView.doRender: Added Group to Toolbar :",this,t,'=',b.text); } } *************** *** 141,158 **** var currGroups = []; ! // Create a specific style to indent rows ! //@TODO come up with a better way to do this with a dummy column ! /* ! var st = Ext.get(gidPrefix+"-style"); ! if(st) st.remove(); ! Ext.getDoc().child("head").createChild({ ! tag:'style', ! id:gidPrefix+"-style", ! html:"div#"+gidPrefix+" div.x-grid3-row {padding-left:"+(gfLen*12)+"px}"+ ! "div#"+gidPrefix+" div.x-grid3-header {padding-left:"+(gfLen*12)+"px}" ! }); ! */ ! ! // Loop through all rows in srecord set for (var i = 0, len = rs.length; i < len; i++) { added = 0; --- 137,141 ---- var currGroups = []; ! // Loop through all rows in record set for (var i = 0, len = rs.length; i < len; i++) { added = 0; *************** *** 238,241 **** --- 221,232 ---- }//for i + // Flag the last groups as incomplete if more rows are available + //NOTE: this works if the associated store is a MultiGroupingPagingStore! + for (var gfi = 0; gfi < gfLen; gfi++) { + var c = currGroups[groupField[gfi]]; + if(this.grid.store.nextKey) c.incomplete=true; + //console.debug("Final Groups are...",c); + } + var buf = []; var toEnd = 0; *************** *** 244,248 **** var g = groups[ilen]; var leaf = g.groupName == groupField[gfLen - 1] ! this.doGroupStart(buf, g, cs, ds, colCount); if (g.rs.length != 0 && leaf) buf[buf.length] = Ext.grid.GroupingView.superclass.doRender.call(this, cs, g.rs, ds, g.startRow, colCount, stripe); --- 235,239 ---- var g = groups[ilen]; var leaf = g.groupName == groupField[gfLen - 1] ! this.doMultiGroupStart(buf, g, cs, ds, colCount); if (g.rs.length != 0 && leaf) buf[buf.length] = Ext.grid.GroupingView.superclass.doRender.call(this, cs, g.rs, ds, g.startRow, colCount, stripe); *************** *** 259,263 **** } for (var k = 0; k < toEnd; k++) { ! this.doGroupEnd(buf, g, cs, ds, colCount); } toEnd = jj; --- 250,254 ---- } for (var k = 0; k < toEnd; k++) { ! this.doMultiGroupEnd(buf, g, cs, ds, colCount); } toEnd = jj; *************** *** 268,272 **** --- 259,296 ---- } + /** Initialize new templates */ + ,initTemplates: function() { + Ext.ux.grid.MultiGroupingView.superclass.initTemplates.call(this); + + if (!this.startMultiGroup) { + this.startMultiGroup = new Ext.XTemplate('<div id="{groupId}" class="x-grid-group {cls}">', '<div id="{groupId}-hd" class="x-grid-group-hd" style="{style}"><div>', this.groupTextTpl, '</div></div>', '<div id="{groupId}-bd" class="x-grid-group-body">'); + } + this.startMultiGroup.compile(); + this.endMultiGroup = '</div></div>'; + } + /** Private - Selects a custom group template if one has been defined + */ + ,doMultiGroupStart: function(buf, g, cs, ds, colCount) { + var groupName = g.groupName, tpl=null; + + if (this.groupFieldTemplates) { + tpl = this.groupFieldTemplates[groupName]; + //console.debug("doMultiGroupStart: Template for group ",groupName, tpl); + if (tpl && typeof(tpl) == 'string') { + tpl = new Ext.XTemplate('<div id="{groupId}" class="x-grid-group {cls}">', '<div id="{groupId}-hd" class="x-grid-group-hd" style="{style}"><div>', tpl, '</div></div>', '<div id="{groupId}-bd" class="x-grid-group-body">'); + tpl.compile(); + this.groupFieldTemplates[groupName]=tpl; + } + } + if(tpl) + buf[buf.length] = tpl.apply(g); + else + buf[buf.length] = this.startMultiGroup.apply(g); + } + + ,doMultiGroupEnd: function(buf, g, cs, ds, colCount) { + buf[buf.length] = this.endMultiGroup; + } /** Should return an array of all elements that represent a row, it should bypass *************** *** 274,300 **** */ ,getRows: function(){ ! ! // This function is called may times, so use a cache if it is available ! if(this.rowsCache) ! r = this.rowsCache.slice(0); ! else { ! //alert('getRows'); ! if (!this.enableGrouping) { ! return Ext.grid.GroupingView.superclass.getRows.call(this); ! } ! var groupField = this.getGroupField(); ! var r = []; ! var g, gs = this.getGroups(); ! // this.getGroups() contains an array of DIVS for the top level groups ! //console.debug("Get Rows", groupField, gs); ! r = this.getRowsFromGroup(r, gs, groupField[groupField.length - 1]); ! ! // Clone the array, but not the objects in it ! //@TODO uncomment this for caching to work ! this.rowsCache = r.slice(0); ! } ! //console.debug("Found ", r.length, " rows"); ! return r; } --- 298,323 ---- */ ,getRows: function(){ ! var r = []; ! // This function is called may times, so use a cache if it is available ! if(this.rowsCache) ! r = this.rowsCache; ! else { ! //alert('getRows'); ! if (!this.enableGrouping) { ! return Ext.grid.GroupingView.superclass.getRows.call(this); ! } ! var groupField = this.getGroupField(); ! var g, gs = this.getGroups(); ! // this.getGroups() contains an array of DIVS for the top level groups ! //console.debug("Get Rows", groupField, gs); ! r = this.getRowsFromGroup(r, gs, groupField[groupField.length - 1]); ! ! // Clone the array, but not the objects in it ! //@TODO uncomment this for caching to work ! this.rowsCache = r; ! } ! //console.debug("Found ", r.length, " rows"); ! return r; } *************** *** 330,332 **** --- 353,361 ---- return r; } + + /** Override the onLoad, as it always scrolls to the top, we only + * want to do this for an initial load or reload. There is a new event registered in + * the constructor to do this + */ + ,onLoad : function() {} }); Index: MultiGroupingStore.js =================================================================== RCS file: /cvsroot/jaffa/JaffaRIA/source/html/js/extjs/ux/grid/MultiGroupingStore.js,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** MultiGroupingStore.js 29 Aug 2008 22:05:08 -0000 1.2 --- MultiGroupingStore.js 27 Sep 2008 01:01:00 -0000 1.3 *************** *** 20,27 **** } ,sortInfo: [] ,sort: function(field, dir){ ! // alert('sort '+ field); var f = []; if (Ext.isArray(field)) { --- 20,30 ---- } + /* ---------------------------------------------------------------------- + Below is needed for Multi Grouping + ---------------------------------------------------------------------- */ ,sortInfo: [] ,sort: function(field, dir){ ! console.debug('sort on column ',field); var f = []; if (Ext.isArray(field)) { *************** *** 61,65 **** this.fireEvent("datachanged", this); } else { ! if (!this.load(this.lastOptions)) { if (st) { this.sortToggle[f[0].name] = st; --- 64,68 ---- this.fireEvent("datachanged", this); } else { ! if (!this.reload()) { if (st) { this.sortToggle[f[0].name] = st; *************** *** 148,159 **** ,applySort: function(){ - //alert('applySort '); - var si = this.sortInfo; - if (si && si.length > 0 && !this.remoteSort) { this.sortData(si, si[0].direction); } - if (!this.groupOnSort && !this.remoteGroup) { var gs = this.getGroupState(); --- 151,158 ---- *************** *** 166,175 **** ,getGroupState: function(){ ! // alert('getGroupState '+ this.groupField); return this.groupOnSort && this.groupField !== false ? (this.sortInfo ? this.sortInfo : undefined) : this.groupField; } ,sortData: function(flist, direction) { ! //alert('sortData '+ direction); direction = direction || 'ASC'; var st = []; --- 165,174 ---- ,getGroupState: function(){ ! //console.debug('getGroupState ', this.groupField); return this.groupOnSort && this.groupField !== false ? (this.sortInfo ? this.sortInfo : undefined) : this.groupField; } ,sortData: function(flist, direction) { ! console.debug('sortData ',direction); direction = direction || 'ASC'; var st = []; *************** *** 227,228 **** --- 226,368 ---- }); + + + + /** + * @class Ext.ux.grid.MultiGroupingPagingStore + * @extends Ext.ux.grid.MultiGroupingStore + * A specialized {@link Ext.data.Store} that allows data to be appended a page at + * a time as the user scrolls through. It is based on performing server-side sorting + * and grouping and should be used in conjunction with a {@link Ext.ux.grid.MultiGroupPagingGrid} + * @constructor + * Create a new MultiGroupingPagingStore + * @param {Object} config The config object + * + * @author PaulE + */ + Ext.ux.grid.MultiGroupingPagingStore = Ext.extend(Ext.ux.grid.MultiGroupingStore, { + + /** When creating the store, register an internal callback for post load processing + */ + constructor: function(config) { + Ext.ux.grid.MultiGroupingPagingStore.superclass.constructor.apply(this, arguments); + // When loading has finished, need to see if there are more records + /* + this.on("load", function(store, r, options) { + return this.loadComplete(r, options); + }, this);*/ + this.remoteSort=true; + this.remoteGroup=true; + } + + /** + * @cfg {Number} pageSize + * The number of records to read/display per page (defaults to 20) + */ + ,pageSize: 20 + + /** Private: The Key of the extra record read if there is more that the page size + */ + ,nextKey: null + + /** Override the load method so it can merge the groupFields and sortField + * into a single sort criteria (group fields need to be sorted by first!) + */ + ,load : function(options){ + options = options || {}; + if(this.fireEvent("beforeload", this, options) !== false){ + this.storeOptions(options); + var p = Ext.apply(options.params || {}, this.baseParams); + var sort=[]; + if(this.groupField && this.remoteGroup){ + if(Ext.isArray(this.groupField)) + sort[sort.length] = this.groupField.join(","); + else + sort[sort.length] = this.groupField; + } + if(this.sortInfo && this.remoteSort){ + if(Ext.isArray(this.sortInfo)) + for(var i=0;i<this.sortInfo.length;i++) + sort[sort.length]=this.sortInfo[i].field + " " + this.sortInfo[i].direction; + else + sort[sort.length]=this.sortInfo.field + " " + this.sortInfo.direction; + } + p[this.paramNames.sort]=sort.join(","); + console.debug("Store.load : Query Parameters ",p,sort,this.sortInfo.field,this.sortInfo.direction, this); + this.proxy.load(p, this.reader, this.loadRecords, this, options); + return true; + } else { + return false; + } + } + + /** Reload the current set of record, using by default the current options + * This will reload the same number of records that have currently been loaded, not + * just the initial page again. + * @param options, additional query options that can be provided if needed + */ + ,reload : function(options){ + var o = Ext.applyIf(options||{}, this.lastOptions); + var pn = this.paramNames; + o.params[pn.start] = 1; + o.params[pn.limit] = Math.max(this.pageSize,this.data.length) + 1; + o.add = false; + o.initial = false; + console.debug("Store.reload :",o,this.sortInfo); + return this.load(o); + } + /** Load the next page of records, if there are more available + * @param initial, set to true if this should be a initial load + */ + ,loadMore : function(initial){ + if(!initial && !this.nextKey) { + console.debug("Store.loadMore : Reject load, no more records left"); + return; + } + if(initial) delete this.nextKey; + var o = {}, pn = this.paramNames; + o[pn.start] = initial?1:this.getCount()+1; + o[pn.limit] = this.pageSize + 1; + console.debug("Store.loadMore : Loading based on ",o); + this.load({params:o,add:!initial,initial:initial}); + } + + /** Private - Override default callback handler once records have been loaded. + * Looks to see if we are able to find more that just the page size, if so + * it removes the extra one, but keeps it for consistency checking for when the + * next page is loaded + * @param r, array of records read from the server + * @param options, the options that were used by the load operation to do the query + */ + ,loadRecords : function(o, options, success){ + if(o && success && o.records) { + var r=o.records; + console.debug("Store.loadRecords : rows=", r.length, options); + var nextKey = this.nextKey; + delete this.nextKey; + // Need to compare the prior next key, to the first row that was added + // This could trigger a complete reload + if(nextKey) { + var id = this.reader.id; // Get key field name from reader + console.debug("Store.loadRecords : Refresh Check...",r[0][id],nextKey[id]); + if(r[0][id] != nextKey[id]) { + console.debug("Store.loadRecords : Need to refresh all records as they are out of sync"); + } + } + // Need to remove the extra record, and put it in the next key. + if(r.length>=options.params[this.paramNames.limit]) { + console.debug("Store.loadRecords : More records exist, remove extra one"); + this.nextKey = r[r.length-1]; + // remove this last record + r.remove(this.nextKey); + console.debug("Store.loadRecords : Total=",this.data.length,this.getCount()); + } else + // Set the total count as we now know what it is + this.totalCount = this.getCount()+r.length; + } + Ext.ux.grid.MultiGroupingStore.superclass.loadRecords.call(this, o, options, success); + } + }); + + + |