PDA

View Full Version : Simple GridTotals plugin



Animal
16 Sep 2009, 11:48 AM
If Ext.ux.grid.GroupSummary is too complex, and all you need is just a total row containing the sum of numeric columns, then just use



plugins: [ new Ext.ux.GridTotals() ]




/**
* @class Ext.ux.GridTotals
* <p>Copyright Nige (Animal) White and Athena Capital Research, LLC. This software may be used under
* the terms of the <a href="http://en.wikipedia.org/wiki/BSD_licenses">BSD licence</a></p>
* <p>Displays a fixed row at the bottom of the scroller containing totals of all
* numeric columns.</p>
*/
Ext.ux.GridTotals = Ext.extend(Ext.util.Observable, {

init: function(g) {
var v = g.getView();
g.gridTotals = this;
this.grid = g;
this.store = g.getStore();
this.store.on({
reconfigure: this.onGridReconfigure,
scope: this
});
v.updateTotals = this.updateTotals;
v.onLayout = v.onLayout.createSequence(this.onLayout);
v.onAllColumnWidthsUpdated = v.onAllColumnWidthsUpdated.createSequence(this.updateTotals);
},

onLayout: function() {
this.updateTotals();
this.scroller.setHeight(this.scroller.getHeight() - this.totalsRow.getHeight());
},

updateTotals: function() {
if (!this.totalsRow) {
this.mainWrap.setStyle('position', 'relative');
this.totalsRow = this.templates.row.append(this.mainWrap, {
tstyle: 'width:' + this.getTotalWidth(),
cells: ''
}, true);
this.totalsRow.addClass('x-grid-total-row');
this.totalsTr = this.totalsRow.child('tr').dom;
}
var cs = this.getColumnData();
var totals = new Array(cs.length);
var store = this.grid.store;
var fields = store.recordType.prototype.fields;
for (var i = 0, l = this.grid.store.getCount(); i < l; i++) {
var rec = store.getAt(i);
for (var c = 0, nc = cs.length; c < nc; c++) {
var f = cs[c].name;
var t = fields.get(f).type;
if (t == 'int' || t == 'float') {
var v = rec.get(f);
if (Ext.isDefined(totals[c])) {
totals[c] += v;
} else {
totals[c] = v;
}
}
}
}
var cells = '', p = {};
for (var c = 0, nc = cs.length, last = nc - 1; c < nc; c++) {
p.id = cs[c].id;
p.style = cs[c].style;
p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
var v = Ext.isDefined(totals[c]) ? totals[c] : '';
cells += this.templates.cell.apply(Ext.apply({
value: cs[c].renderer(v, p)
}, cs[c]));
}
Ext.DomHelper.insertHtml('afterBegin', this.totalsTr, cells);
},

onGridReconfigure: Ext.emptyFn
});

mncarbone
25 Sep 2009, 2:01 PM
What about a version of this plugin for Ext2.0?

twaindev
26 Sep 2009, 12:38 PM
Just what I needed :)

MmarcoM
27 Sep 2009, 12:50 PM
Hello ANimal,
would you mind posting a simple example?
i have tried to incorporate your GridTotalsp lugin in my grid (where i am doing grouping) and somehow
somehow when i do i lose all grid data (it just shows one group with no rows.)
I might be missing some other settings......

if you can post a small example, whenever you have time, it would be appreciated

thanks and regards
marco

Animal
28 Sep 2009, 1:53 AM
plugins: [ new Ext.ux.GridTotals() ]

wemerson.januario
28 Dec 2009, 4:54 AM
nice work

brycekmartin
4 Mar 2010, 7:31 AM
Hey Animal,
I just thought that I'd let you know that your total row doesn't scroll with the grid horizontally if the view has more data than can be displayed.

I thought maybe other people might want to know before trying this out.

There is another plugin called gridSummary that does this, and I was just working with Marc(mystix) this week and we got the scroll bar go to below the total row, instead of between the grid view and the total row. Just thought I'd give you the heads up.

Thanks for all that you do.
Bryce

Animal
4 Mar 2010, 7:52 AM
Hmm, yes, I haven't set it up to do that. The original client requirement was within a for where the columns fitted.

It should be easy.

Animal
4 Mar 2010, 9:02 AM
Try this:



/**
* @class Ext.ux.GridTotals
* <p>Copyright Nige (Animal) White and Athena Capital Research, LLC. This software may be used under
* the terms of the <a href="http://en.wikipedia.org/wiki/BSD_licenses">BSD licence</a></p>
* <p>Displays a fixed row at the bottom of the scroller containing totals of all
* numeric columns.</p>
*/
Ext.ux.GridTotals = Ext.extend(Ext.util.Observable, {

init: function(g) {
g.cls = (g.cls || '') + 'x-grid3-simple-totals';
var v = g.getView();
g.gridTotals = this;
this.grid = g;
this.store = g.getStore();
this.store.on({
reconfigure: this.onGridReconfigure,
scope: this
});
v.updateTotals = this.updateTotals;
v.fixScrollerPosition = this.fixScrollerPosition;
v.onLayout = v.onLayout.createSequence(this.onLayout);
v.initElements = v.initElements.createSequence(this.initElements);
v.onAllColumnWidthsUpdated = v.onAllColumnWidthsUpdated.createSequence(this.onLayout);
v.onColumnWidthUpdated = v.onColumnWidthUpdated.createSequence(this.onLayout);
},

initElements: function() {
var me = this;
this.scroller.on('scroll', function() {
me.totalsRow.setStyle({
left: -me.scroller.dom.scrollLeft + 'px'
});
});
},

onLayout: function() {
this.updateTotals();
this.fixScrollerPosition();
},

fixScrollerPosition: function() {
var bottomScrollbarWidth = this.scroller.getHeight() - this.scroller.dom.clientHeight;
this.totalsRow.setStyle({
bottom: bottomScrollbarWidth + 'px',
width: Math.min(this.mainBody.getWidth(), this.scroller.dom.clientWidth) + 'px'
});
},

updateTotals: function() {
if (!this.totalsRow) {
this.mainWrap.setStyle('position', 'relative');
this.totalsRow = this.templates.row.append(this.mainWrap, {
tstyle: 'width:' + this.mainBody.getWidth(),
cells: ''
}, true);
this.totalsRow.addClass('x-grid-total-row');
this.totalsTr = this.totalsRow.child('tr').dom;
}
var cs = this.getColumnData();
var totals = new Array(cs.length);
var store = this.grid.store;
var fields = store.recordType.prototype.fields;
for (var i = 0, l = this.grid.store.getCount(); i < l; i++) {
var rec = store.getAt(i);
for (var c = 0, nc = cs.length; c < nc; c++) {
var f = cs[c].name;
var t = fields.get(f).type;
if (t == 'int' || t == 'float') {
var v = rec.get(f);
if (Ext.isDefined(totals[c])) {
totals[c] += v;
} else {
totals[c] = v;
}
}
}
}
var cells = '', p = {};
for (var c = 0, nc = cs.length, last = nc - 1; c < nc; c++) {
p.id = cs[c].id;
p.style = cs[c].style;
p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
var v = Ext.isDefined(totals[c]) ? totals[c] : '';
cells += this.templates.cell.apply(Ext.apply({
value: cs[c].renderer(v, p)
}, cs[c]));
}
Ext.DomHelper.overwrite(this.totalsTr, cells);
},

onGridReconfigure: Ext.emptyFn
});


with this:



.x-grid3-simple-totals .x-grid3-row-last {
margin-bottom: 21px;
}

.x-grid3-simple-totals .x-grid-total-row {
position: absolute;
left: 0;
bottom: 15px;
background: #F9F9F9 url(../../resources/images/default/grid/grid3-hrow.gif);
}

.x-grid3-simple-totals .x-grid-total-row td {
border-left: 1px solid #EEEEEE;
border-right: 1px solid #D0D0D0;
padding-left: 0px;
padding-right: 0px;
}

Monkee
10 Mar 2010, 2:53 AM
I have a situation in which I have to reload the store multiple times for the same grid.
What happens is that everytime I load the store, another set of table cells is being added to the total row.
So instead of inserting new cells, I overwrite the old content with new one, as it makes more sense.


Try to modify the line


Ext.DomHelper.insertHtml('afterBegin', this.totalsTr, cells);with this


Ext.DomHelper.overwrite(this.totalsTr, cells);

Animal
10 Mar 2010, 2:56 AM
Good one. I'll change it.

Monkee
10 Mar 2010, 3:24 AM
When a store is reloaded, it would trigger sometimes the resize event for the grid, but the width of the total row will remain the same as the first time the grid was rendered. In order to solve that problem:


onLayout: function() {
this.updateTotals();
this.scroller.setHeight(this.scroller.getHeight() - this.totalsRow.getHeight());
this.totalsRow.setWidth(this.getTotalWidth());
this.totalsRow.child('table').setWidth(this.getTotalWidth());
}

Animal
10 Mar 2010, 4:26 AM
What's the changed or added code there? Highlight your changes or additions in red.

yogeshmsharma
11 Mar 2010, 2:55 AM
Can I render it for editorGridPanel , and keep on adding if user enter value .

brycekmartin
11 Mar 2010, 5:58 AM
Hey Animal,
It didn't quite work. It got the scroll bar below the grid total, but there is a hiccup somewhere. Not all the cells get styled and the styled cells overlay the bottom of the vertical scrollbar...also, the numbers look as if they are passing over the scroll bar as opposed to behind it... I can put my code in a dropbox and post the link if you'd wish.

vaucer
10 Apr 2010, 7:11 AM
Hello,

I tried to make a modification on this plugin. I add a locked cell with named total and i add manually three summary cell based on two summary cells automatically calculated by the plugin.

But i get a additionnal cell on my summary row which contain my cell named total looped:

The code modified:

Ext.ux.GridTotals = Ext.extend(Ext.util.Observable, {
init: function(g) {
var v = g.getView();
g.gridTotals = this;
this.grid = g;
this.store = g.getStore();
this.store.on({
reconfigure: this.onGridReconfigure,
scope: this
});
v.updateTotals = this.updateTotals;
v.onLayout = v.onLayout.createSequence(this.onLayout);
v.onAllColumnWidthsUpdated = v.onAllColumnWidthsUpdated.createSequence(this.updateTotals);
},
onLayout: function() {
this.updateTotals();
this.scroller.setHeight(this.scroller.getHeight() - this.totalsRow.getHeight());
},
updateTotals: function() {
if (!this.totalsRow) {
this.mainWrap.setStyle('position', 'relative');
this.totalsRow = this.templates.row.append(this.mainWrap, {
tstyle: 'width:' + this.getTotalWidth(),
cells: ''
}, true);
this.totalsRow.addClass('x-grid-total-row');
this.totalsTr = this.totalsRow.child('tr').dom;
}
var cs = this.getColumnData();
var totals = new Array(cs.length);
var store = this.grid.store;
var fields = store.recordType.prototype.fields;
for (var i = 0, l = this.grid.store.getCount(); i < l; i++) {
var rec = store.getAt(i);
for (var c = 0, nc = cs.length; c < nc; c++) {
var f = cs[c].name;
var t = fields.get(f).type;
if (t == 'int' || t == 'float') {
var v = rec.get(f);
if (Ext.isDefined(totals[c])) {
totals[c] += v;
} else {
totals[c] = v;
}
}
}
}

var cells = '', p = {};
cells += this.templates.cell.apply(Ext.apply({
value: 'Total'
}, cs[0]));
for (var c = 1, nc = cs.length-3, last = nc -1; c < nc; c++) {
p.id = cs[c].id;
p.style = cs[c].style;
p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
var v = Ext.isDefined(totals[c]) ? totals[c] : '';
cells += this.templates.cell.apply(Ext.apply({
value: cs[c].renderer(v, p)
}, cs[c]));
}
var tot = cs[1].renderer(totals[1], p) + cs[2].renderer(totals[2], p);
cells += this.templates.cell.apply(Ext.apply({
value: tot
}, cs[3]));
cells += this.templates.cell.apply(Ext.apply({
value: tot
}, cs[4]));
cells += this.templates.cell.apply(Ext.apply({
value: tot
}, cs[5]));

Ext.DomHelper.insertHtml('afterBegin', this.totalsTr, cells);
},
onGridReconfigure: Ext.emptyFn
});

And the result on my grid:

19867

I would like just remove this additionnal Total cell in the right down corner.

Thx you very much for your help

And sorry form my little level and my bad english

Vaucer

r_honey
20 Apr 2010, 10:33 PM
I am getting a js error trying to use this plugin. The error occurs in the call to Ext.DomHelper.overwrite method (It occurs in the createHtml(o) line inside that method).

The following is the value of o at the point of error:


<td class="x-grid3-col x-grid3-cell x-grid3-td-ext-gen1026 " style="width:28px;" tabIndex="0" ><div class="x-grid3-cell-inner x-grid3-col-ext-gen1026" unselectable="on" ></div></td><td class="x-grid3-col x-grid3-cell x-grid3-td-1 " style="width:98px;" tabIndex="0" ><div class="x-grid3-cell-inner x-grid3-col-1" unselectable="on" >&#160;</div></td><td class="x-grid3-col x-grid3-cell x-grid3-td-2 " style="width:73px;" tabIndex="0" ><div class="x-grid3-cell-inner x-grid3-col-2" unselectable="on" >&#160;</div></td><td class="x-grid3-col x-grid3-cell x-grid3-td-3 " style="width:73px;" tabIndex="0" ><div class="x-grid3-cell-inner x-grid3-col-3" unselectable="on" >&#160;</div></td><td class="x-grid3-col x-grid3-cell x-grid3-td-4 " style="width:73px;" tabIndex="0" ><div class="x-grid3-cell-inner x-grid3-col-4" unselectable="on" >&#160;</div></td><td class="x-grid3-col x-grid3-cell x-grid3-td-5 " style="width:73px;" tabIndex="0" ><div class="x-grid3-cell-inner x-grid3-col-5" unselectable="on" >&#160;</div></td><td class="x-grid3-col x-grid3-cell x-grid3-td-6 " style="width:73px;" tabIndex="0" ><div class="x-grid3-cell-inner x-grid3-col-6" unselectable="on" >&#160;</div></td><td class="x-grid3-col x-grid3-cell x-grid3-td-7 " style="width:73px;" tabIndex="0" ><div class="x-grid3-cell-inner x-grid3-col-7" unselectable="on" >&#160;</div></td><td class="x-grid3-col x-grid3-cell x-grid3-td-8 " style="width:73px;" tabIndex="0" ><div class="x-grid3-cell-inner x-grid3-col-8" unselectable="on" ></div></td><td class="x-grid3-col x-grid3-cell x-grid3-td-9 " style="width:73px;" tabIndex="0" ><div class="x-grid3-cell-inner x-grid3-col-9" unselectable="on" >&#160;</div></td><td class="x-grid3-col x-grid3-cell x-grid3-td-10 " style="width:98px;" tabIndex="0" ><div class="x-grid3-cell-inner x-grid3-col-10" unselectable="on" >&#160;</div></td>

r_honey
21 Apr 2010, 1:45 AM
Okay, I debugged the code further to notice that the problem is not the createHtml method. It returns the same string as was passed to it. The problem is the assignment of the returned string as the el's innerHTML, i.e. the problem is the following assignment:

el.innerHTML = createHtml(o);

r_honey
21 Apr 2010, 2:29 AM
Okay, I found the solution. The problem was the Ext.DomHelper.overwrite method. For some reason, that method when used on <tr> gave the js error. My solution was to restore Animal's original Ext.DomHelper.insertHtml method, and add the following code to supplement it:



while (this.totalsTr.hasChildNodes()) {
this.totalsTr.removeChild(this.totalsTr.lastChild);
}
Ext.DomHelper.insertHtml('afterBegin', this.totalsTr, cells);

r_honey
21 Apr 2010, 7:59 PM
Well, I needed to use this plugin with Coolite/Ext.net (which is a server-side wrapper library for ExtJs).

I had to make a couple of changes to make this plugin work with Ext.net, plus I added support for displaying static text as summary for non-int and non-float fields. I have discussed my changes to this plugin here (http://www.rahulsingla.com/blog/2010/04/ext-net-gridpanel-columns-summary-plugin-(without-grouping)).

I also added support for maintaining running totals, so that the totals update when data in the grid changes.

Here's my adapted code:



Ext.ux.GridTotals = Ext.extend(Ext.util.Observable, {

init: function(g) {
g.cls = (g.cls || '') + 'x-grid3-simple-totals';
var v = g.getView();
g.gridTotals = this;
this.grid = g;
this.store = g.getStore();
this.store.on({
reconfigure: { fn: this.onGridReconfigure, scope: this },
add: { fn: this.updateTotals, scope: v },
remove: { fn: this.updateTotals, scope: v },
update: { fn: this.updateTotals, scope: v },
datachanged: { fn: this.updateTotals, scope: v }
});
v.updateTotals = this.updateTotals;
v.fixScrollerPosition = this.fixScrollerPosition;
v.onLayout = v.onLayout.createSequence(this.onLayout);
v.initElements = v.initElements.createSequence(this.initElements);
v.onAllColumnWidthsUpdated = v.onAllColumnWidthsUpdated.createSequence(this.onLayout);
v.onColumnWidthUpdated = v.onColumnWidthUpdated.createSequence(this.onLayout);
},

initElements: function() {
var me = this;
this.scroller.on('scroll', function() {
me.totalsRow.setStyle({
left: -me.scroller.dom.scrollLeft + 'px'
});
});
},

onLayout: function() {
this.updateTotals();
this.fixScrollerPosition();
},

fixScrollerPosition: function() {
var bottomScrollbarWidth = this.scroller.getHeight() - this.scroller.dom.clientHeight;
this.totalsRow.setStyle({
bottom: bottomScrollbarWidth + 'px',
width: Math.min(this.mainBody.getWidth(), this.scroller.dom.clientWidth) + 'px'
});
},

updateTotals: function() {
if (!this.totalsRow) {
this.mainWrap.setStyle('position', 'relative');
this.totalsRow = this.templates.row.append(this.mainWrap, {
tstyle: 'width:' + this.mainBody.getWidth(),
cells: ''
}, true);
this.totalsRow.addClass('x-grid-total-row');
this.totalsTr = this.totalsRow.child('tr').dom;
}
var cs = this.getColumnData();
var totals = new Array(cs.length);
var store = this.grid.store;
var fields = store.recordType.prototype.fields;
var columns = this.cm.columns;
for (var i = 0, l = this.grid.store.getCount(); i < l; i++) {
var rec = store.getAt(i);
for (var c = 0, nc = cs.length; c < nc; c++) {
var f = cs[c].name;
var t = !Ext.isEmpty(f) ? fields.get(f).type : '';
if (t.type == 'int' || t.type == 'float') {
var v = rec.get(f);
if (Ext.isDefined(totals[c])) {
totals[c] += v;
} else {
totals[c] = v;
}
} else if (columns[c].totalsText) {
totals[c] = columns[c].totalsText;
}
}
}
var cells = '', p = {};
for (var c = 0, nc = cs.length, last = nc - 1; c < nc; c++) {
p.id = cs[c].id;
p.style = cs[c].style;
p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
var v = Ext.isDefined(totals[c]) ? totals[c] : '';
cells += this.templates.cell.apply(Ext.apply({
value: cs[c].renderer(v, p, undefined, undefined, c, store)
}, cs[c]));
}
while (this.totalsTr.hasChildNodes()) {
this.totalsTr.removeChild(this.totalsTr.lastChild);
}
Ext.DomHelper.insertHtml('afterBegin', this.totalsTr, cells);
},

onGridReconfigure: Ext.emptyFn
});

calavera
28 Apr 2010, 5:00 AM
Hello. I am using ExtJS 3.0 and I can't get this plugin to work. When I access the grid page, I get this error on firebug:

fields.get(f) is undefined on line 68 which reads:
var t = fields.get(f).type;

I'm also using the RowActions plugin and I get this error too:
record is undefined on line 375 which reads:
return record.data || {};

The RowActions plugin works great if I disable the GridTotals plugin. Also, the GridTotals plugin does not work without the RowActions plugin disabled. So, any ideas why I'm getting this ?

Thank you.

r_honey
28 Apr 2010, 6:01 AM
Hi calavera, if you notice my previous post (http://www.extjs.com/forum/showthread.php?80579-Simple-GridTotals-plugin&p=460356#post460356),
you would notice that I also had the same problem for some columns (the code in red).

The solution was to put an extra check on f for being empty.

mayurid
15 Jun 2010, 9:25 PM
hi animal,,
pls help

would u pls post simple grouping grid with row summary. jus lik totals.html from samples i am using json, php,mysql..
any example..

RonaldBrinkerink
31 Aug 2011, 4:01 AM
Hi,

i've added the 'average' summaryType option so the plugin can be combined with the groupsummary plugin.


getTotals: function() {
....
switch (columns[c].summaryType) {
......
// Average added by Ronald Brinkerink
case 'average':
totals[c] = ((totals[c] * i) + v) / (i + 1);
break;
}
....
}

talha06
7 Oct 2011, 7:15 AM
Thanks Animal and community for this useful plugin. :) I'm having same problem with calavera. As he mentioned above (http://www.sencha.com/forum/showthread.php?80579-Simple-GridTotals-plugin&p=462440&viewfull=1#post462440), I'm not able to use this plugin with RowActions. Also I tried r_honey's adds but still having same errors. :-?

I'll be happy if someone can help..
Thanks in advance..