-
20 Nov 2008 4:15 AM #11
-
5 Jan 2009 5:50 PM #12
total row grid at the bottom
total row grid at the bottom
hi sydnic,
can u give me an exact example code the you have said about the ff:
1. Create two grids, one to hold the data, the other holds the totals
2. Each grid has its own data store
3. Both grids use the same ColumnModel
4. Add two toolbars to the bottom of the data grid, the first toolbar holds the totals grid, the second is the paging toolbar.
tnx.tnx. i really do appreciate your assistance!
-
7 Jan 2009 6:51 PM #13
hi sydnic,
can u give me/provide an exact example code the you have said about the ff:
1. Create two grids, one to hold the data, the other holds the totals
2. Each grid has its own data store
3. Both grids use the same ColumnModel
4. Add two toolbars to the bottom of the data grid, the first toolbar holds the totals grid, the second is the paging toolbar.
tnx.tnx. i really do appreciate your assistance!
-
18 Feb 2009 6:47 PM #14
Another method with one store
Another method with one store
A one Store solution - interested in feedback:
I am not using grouping - but want totals and want them to come from the server.
The solution overview:
- Update the server process to compute totals for desired columns and return an added record at the end with the totals.
- In this new record - a new field called _sort contains 'bottom' (or 'top'). This tells the system where to keep the totals placed.
- Update the meta to have this new field - but make hidden.
- Extend the Ext.data.JsonStore object and override the sortData method / add a couple helper functions.
- Add a css and implement a viewConfig.getRowClass to return the class for the total line
The solution details:
Since the server is sending the totals as a final row - the solution was to simply make the row stay at the top or bottom of the grid. All that is needed is another field called '_sort' that can be set to 'bottom' or 'top'.
Then the overridden sort function (shown below) will find and remove them before the sort - then add them back to the top or the bottom of the sort - before it renders.
Note: Since this is a new record field - I had to add it to the meta as well. Just added this to the end of the meta field list
Added to meta:
Here is the extended JsonStore (2.2)Code:{name: '_sort', hidden: true}
Code:TotalsStore = Ext.extend(Ext.data.JsonStore, { findSortBottom: function(record,id){ return (record.get('_sort') == 'bottom') }, findSortTop: function(record,id){ return (record.get('_sort') == 'top') }, sortData : function(f, direction){ direction = direction || 'ASC'; var st = this.fields.get(f).sortType; //--- Update 1: Find and remove records tagged as top and bottom entries var myBottom = this.findBy( this.findSortBottom ); var myTop = this.findBy( this.findSortTop ); var myBottomRec,myTopRec if (myBottom > -1 ){ myBottomRec = this.getAt(myBottom); this.remove(myBottomRec); } if (myTop > -1 ){ myTopRec = this.getAt(myTop); this.remove(myTopRec); } //--- End Update 1 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); //--- Update 2: Add top and bottom record if needed if( myTopRec ){ this.insert(0,myTopRec) } if( myBottomRec ){ this.add(myBottomRec) }; //--- End Update 2 if(this.snapshot && this.snapshot != this.data){ this.snapshot.sort(direction, fn); } } })
About styling:
We want the total to look nice. Lucky for us ExtJS thinks was thinking ahead and they have the great getRowClass method. This method, when you implement it, will return a class to be added to the row based on the record content. Since the flag for sorting / total is a field now - it was simple enough to add a GridPanel getRowClass define as shown below.
Added Style:
Added viewCofig to the GridPanel. This adds the above style only for totals:Code:.grid-totals-row td{ font-weight: bold; border-top:1px solid black; border-bottom:1px solid black; }
The end result:Code:viewConfig: { getRowClass: function(record,index,rowParams,store) { if ( (record.get('_sort')) ){ return 'grid-totals-row' } } }
A grid that can get totals from the server and show them on the top or the bottom of the grid. They will resize with the normal columns. The total line can be easily styled via css.
Note: Be sure to sort after you load if the totals are not already in the location (top/bottom) when fed out (i.e. at bottom when you send but want to show on top).
CheersLast edited by Joe; 18 Feb 2009 at 8:15 PM. Reason: Updated to use extended JsonStore
Joseph Francis,
CoreLan / Meeting Consultants
-
18 Feb 2009 8:18 PM #15
instead of having to send a _sort parameter down from the server you might want to rip the renderSummary() method out of the ux.GridSummary and modify it to suit your needs instead:
http://extjs.com/forum/showthread.php?t=21331
having to send down a _sort param makes your data tightly coupled, no?
Sencha Docs / Ext 3.x - ( Docs | Examples )
Learning Center / Saki's Examples (for 2.x) / HOWTO - ( Report Bugs | Post Proper Code )
-
18 Feb 2009 8:39 PM #16
I was trying that ...
I was trying that ...
Thanks for the input and link. I started with that but had no luck making it work. Any help (by anyone) in implementing such a solution would be much appreciated. I will post updates if I do so.
As for being "tightly coupled' - not really. I am not adding this to any of my data or even to the data records passed back - just on the one final record (and the one meta entry for the hidden field). I send lots of params into my data feed already so adding another param for top or bottom was easy enough as well. I can find the total locally via JS with nothing from the server as well - just wanted to have the option for the server to make such a decision if desired.
That said - I like the idea of using a solution much like the link you pointed at - just no time.
CheersJoseph Francis,
CoreLan / Meeting Consultants
-
5 Jan 2010 1:24 AM #17
I modified the GridSummary plugin to read the totals from a 'totals' property that comes from the JSON response:
{results:[], totals:{record like the rows with totals}}
PHP Code:GO.grid.RemoteGridTotals = function(config){
Ext.apply(this, config);
};
Ext.extend(GO.grid.RemoteGridTotals , Ext.util.Observable, {
init : function(grid){
this.grid = grid;
this.cm = grid.getColumnModel();
this.view = grid.getView();
var v = this.view;
v.layout = this.layout.createDelegate(this);
v.afterMethod('refresh', this.refreshSummary, this);
v.afterMethod('refreshRow', this.refreshSummary, this);
v.afterMethod('onColumnWidthUpdated', this.refreshSummary, this);
v.afterMethod('onAllColumnWidthsUpdated', this.refreshSummary, this);
v.afterMethod('onColumnHiddenUpdated', this.refreshSummary, this);
v.afterMethod('onUpdate', this.refreshSummary, this);
v.afterMethod('onRemove', this.refreshSummary, this);
if(!this.rowTpl){
this.rowTpl = new Ext.Template(
'<div class="x-grid3-summary-row" style="{tstyle}">',
'<table class="x-grid3-summary-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
'<tbody><tr>{cells}</tr></tbody>',
'</table></div>'
);
this.rowTpl.disableFormats = true;
}
this.rowTpl.compile();
if(!this.cellTpl){
this.cellTpl = new Ext.Template(
'<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}">',
'<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on">{value}</div>',
"</td>"
);
this.cellTpl.disableFormats = true;
}
this.cellTpl.compile();
},
renderSummary : function(o, cs){
cs = cs || this.view.getColumnData();
var cfg = this.cm.config;
var buf = [], c, p = {}, cf, last = cs.length-1;
for(var i = 0, len = cs.length; i < len; i++){
c = cs[i];
cf = cfg[i];
p.id = c.id;
p.style = c.style;
p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
p.value=!Ext.isEmpty(o.data[c.name]) ? o.data[c.name] : '';
if(cf.summaryRenderer){
p.value=cf.summaryRenderer(p.value, p, o);
}
buf[buf.length] = this.cellTpl.apply(p);
}
return this.rowTpl.apply({
tstyle: 'width:'+this.view.getTotalWidth()+';',
cells: buf.join('')
});
},
calculate : function(rs, cs){
var data = {}, r, c, cfg = this.cm.config, cf;
for(var j = 0, jlen = rs.length; j < jlen; j++){
r = rs[j];
for(var i = 0, len = cs.length; i < len; i++){
c = cs[i];
cf = cfg[i];
if(cf && cf.summaryType){
data[c.name] = GO.grid.RemoteGridTotals .Calculations[cf.summaryType](data[c.name] || 0, r, c.name, data);
}
}
}
return data;
},
layout : function(){
if (!this.view.summary)
this.view.summary = Ext.DomHelper.insertAfter(this.view.mainBody.dom.parentNode, {
tag:'div'
}, true);
if(!this.view.mainBody){
return;
}
var g = this.grid;
var c = g.getGridEl(), cm = this.cm,
expandCol = g.autoExpandColumn,
gv = this.view;
var csize = c.getSize(true);
var vw = csize.width;
if(!vw || !csize.height){ // display: none?
return;
}
if(g.autoHeight){
this.view.scroller.dom.style.overflow = 'visible';
}else{
var smHeight = this.view.summary.getHeight();
var hdHeight = this.view.mainHd.getHeight();
this.view.el.setSize(csize.width, csize.height + smHeight);
var vh = csize.height - (hdHeight) - (smHeight);
this.view.scroller.setSize(vw, vh);
this.view.innerHd.style.width = (vw)+'px';
}
if(this.view.forceFit){
if(this.view.lastViewWidth != vw){
this.view.fitColumns(false, false);
this.view.lastViewWidth = vw;
}
}else {
this.view.autoExpand();
}
this.view.onLayout(vw, vh);
},
doWidth : function(col, w, tw){
var gs = this.view.getRows(), s;
for(var i = 0, len = gs.length; i < len; i++){
s = gs[i].childNodes[2];
s.style.width = tw;
s.firstChild.style.width = tw;
s.firstChild.rows[0].childNodes[col].style.width = w;
}
},
doAllWidths : function(ws, tw){
var gs = this.view.getRows(), s, cells, wlen = ws.length;
for(var i = 0, len = gs.length; i < len; i++){
s = gs[i].childNodes[2];
s.style.width = tw;
s.firstChild.style.width = tw;
cells = s.firstChild.rows[0].childNodes;
for(var j = 0; j < wlen; j++){
cells[j].style.width = ws[j];
}
}
},
doHidden : function(col, hidden, tw){
var gs = this.view.getRows(), s, display = hidden ? 'none' : '';
for(var i = 0, len = gs.length; i < len; i++){
s = gs[this.view.getRows()-1].childNodes[2];
s.style.width = tw;
s.firstChild.style.width = tw;
s.firstChild.rows[0].childNodes[col].style.display = display;
}
},
// Note: requires that all (or the first) record in the
// group share the same group value. Returns false if the group
// could not be found.
refreshSummary : function(){
var g = this.grid, cm = g.colModel, ds = g.store, stripe = g.stripeRows;
var colCount = cm.getColumnCount();
if(ds.getCount() < 1){
return "";
}
var cs = this.view.getColumnData();
//var startRow = startRow || 0;
//var endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
// records to render
//var rs = ds.getRange();
var buf = [];
//var data = this.calculate(rs, cs);
var data = ds.reader.jsonData.totals;
buf.push('</div>', this.renderSummary({
data: data
}, cs), '</div>');
this.view.summary.update(buf.join(''));
this.view.layout();
},
getSummaryNode : function(){
return this.view.summary
},
showSummaryMsg : function(groupValue, msg){
var gid = this.view.getGroupId(groupValue);
var node = this.getSummaryNode(gid);
if(node){
node.innerHTML = '<div class="x-grid3-summary-msg">' + msg + '</div>';
}
}
});
Building Group-Office (http://www.group-office.com) with ExtJS: http://http://demo.group-office.eu user: demo / pass: demo
-
21 Dec 2011 5:34 AM #18
I cant make it work; its throwing following error "Uncaught TypeError: Cannot read property '' of undefined" in this line
"p.value=!Ext.isEmpty(o.data[c.name]) ? o.data[c.name] : '';"
I want to use this plugin with direct store and paging. Can you please show how will it work?



Reply With Quote
