PDA

View Full Version : GroupHeaderGrid - Grouped grid column headers



Condor
1 Aug 2009, 6:47 AM
This plugin allows you to add groups of column headers above the original column headers.

Features:
- Unlimited number of header rows.
- Hierachical column menu (optional).
- Column menu supports showing/hiding groups.
- Drag & drop support for groups.

The original Ext 2.x plugin can be found here (http://www.extjs.com/forum/showthread.php?t=22337).

This plugin is only meant for Ext 3.0. In Ext 3.1+ it is included in the SDK examples/ux folder (renamed to ColumnHeaderGroup).

Stuff to remember:
- IE can corrupt the downloaded archive - Use a different browser.
- Don't forget to correct the image locations in the css file.

xevin
6 Aug 2009, 1:30 AM
Condor, do u have 3.0 Version for LockingEditorGridPanel?

Condor
6 Aug 2009, 1:42 AM
Yes, here (http://extjs.com/forum/showthread.php?t=76324).

But I'm not going to create a LockingGroupHeaderGrid component again (unless somebody pays me -- it's just too much work).

MiamiCoder
6 Aug 2009, 2:05 PM
Very nice, Condor. Thank you.

xevin
6 Aug 2009, 4:12 PM
Condor, if ur going to make it, how much is it?

wcoolly
2 Sep 2009, 5:05 PM
how to use the plugin?
I don`t know the file format, I am a beginner

carol.ext
2 Sep 2009, 6:14 PM
how to use the plugin?
I don`t know the file format, I am a beginner

The zip file attachment has an example in it, did you look at it?

wcoolly
2 Sep 2009, 6:17 PM
The zip file attachment has an example in it, did you look at it?
I find only one file in the zip file ,and i don`t know how to open it.

wcoolly
2 Sep 2009, 6:18 PM
The zip file attachment has an example in it, did you look at it?
thank you ,i have find it.

mattpainter
6 Sep 2009, 9:19 PM
Fantastic piece of work. Thanks Condor; works beautifully.

J@y
22 Sep 2009, 4:12 AM
How can I only group the first level of headers (the original column headers)?

In other words, to replace the existing column header instead of adding a header row to represent a group header.

thanks

Condor
22 Sep 2009, 4:25 AM
The original (first-level) headers need to be rendered, because the GridPanel baseclass expects them to be present.

But that doesn't mean you can't hide them with display:none!

J@y
22 Sep 2009, 4:34 AM
The original (first-level) headers need to be rendered, because the GridPanel baseclass expects them to be present.

But that doesn't mean you can't hide them with display:none!

Thanks, so do you mean I've to explicitly get the DOM of header and set the CSS to display:none?

any faster way to achieve this?

PS: I've tried to inspect the DOM in firebug and set the <div> to display:none, the header space is still exist in the <td> ...
If I set the <td> to display:none, the whole header's layout will be corrupted. But I can't add the colspan to the grid original header...

Since this groupHeader plugin renders the header rows in separate <table>s, the alignment of header cells cannot be dependently aligned.

hellogavin
23 Sep 2009, 4:33 AM
Thank you for your share:D~o)

Thomas Triplet
25 Sep 2009, 7:43 AM
Fantastic piece of work. Thanks Condor; works beautifully.

+1000 =D>

calavera
28 Sep 2009, 3:24 AM
Great share! I have noticed that the dataIndex column defined in the GroupHeaderGrid becomes unsortable. Only the first column has this problem.

LE: The next group of header column has no working sortable column...Any fixes ?

Thanks.

Condor
28 Sep 2009, 3:35 AM
Great share! I have noticed that the dataIndex column defined in the GroupHeaderGrid becomes unsortable. Any fix for this ? Only the first column has this problem.

Can you reproduce the problem with the provided example?

If not, can you post an example that does?

Palastanga
2 Oct 2009, 10:10 AM
Thank you so, so much for sharing this! It's exactly what I was looking for. :D

Palastanga
3 Oct 2009, 6:24 AM
Condor:

I'm trying to dynamically update the GroupHeader header value from store JSON data but I'm having problems.

This is because I want the dynamic JSON response from the store to update my Group Column Headers. I'm making a 2 day calendar style grid view - so my previous and next buttons load the store and return data and 2 new dates/days which I want to display in the header.

I currently do this to standard GridPanel columnHeaders by doing either:



myStore.on('load', function() {
myColumnModel.setConfig([
{header: "New Header", dataIndex: 'foo'}
])
}
or with:



myStore.on('load', function() {
myColumnModel.setColumnHeader(1, 'New Header');
}


| Tuesday | Wednesday |
-----------------------------------------------
| 8 | 9 | 10 | 11 | 12 | 8 | 9 | 10 | 11 | 12 |
-----------------------------------------------
I want to change Tuesday/Wednesday dynamically to values from my JSON.

If you have any suggestions for how I could implement this I would be eternally grateful! Sorry if i've rambled on with too useless detail.

Thanks
Martin

Condor
3 Oct 2009, 7:28 AM
Modify grid.getColumnModel().rows and call grid.getView().updateHeaders() (calling grid.getColumnModel().setConfig() will also update the headers).

Palastanga
3 Oct 2009, 9:29 AM
Woohoo! That works perfectly.

Thank you very much for your time and helping me out :)

vtulin
14 Oct 2009, 10:47 PM
Does it support row spanning?

Condor
14 Oct 2009, 11:00 PM
No, it doesn't support row spanning. I tried, but some browsers just wouldn't size the table cells properly.

You can make it look like row spanning by not specifying a header for supergroups (see the Id, Nr and Sum headers in the screenshot).

vtulin
15 Oct 2009, 3:19 AM
And what is the latest version that support row spanning?

Condor
15 Oct 2009, 4:31 AM
Row spanning never worked correctly, even with the versions that did support is.

Do you really need it? Most people find the visual appearance of row spanning enough.

MaximGB
16 Oct 2009, 3:44 AM
I need it, so hardly that writing my own header groupping plugin is in my todo list ;)

Condor
16 Oct 2009, 3:46 AM
I need it, so hardly that writing my own header groupping plugin is in my todo list ;)

Can you explain why you need it? I could revisit the subject if you have a convincing argument.

MaximGB
16 Oct 2009, 4:30 AM
In the project I currently involved in there is a large treegrid with lots of columns, ~30 or more, most of the column titles consist of 2-3 words, but actual cell values don't occupy that much space. We are trying to save as much space as possible by groupping headers, but sometimes a cell with a rather large title can't be groupped with anything else, so I have to create a single column group for it where in the group header I show first part of the actual cell title, and in the cell header I show second part of the cell title.

Condor
16 Oct 2009, 4:40 AM
But you don't need row spanning for that. You can create a column header and a group of the same size and set both titles.

(you probably want to adjust the css so the group title doesn't get the gray background)

vtulin
19 Oct 2009, 3:52 AM
But you don't need row spanning for that. You can create a column header and a group of the same size and set both titles.

(you probably want to adjust the css so the group title doesn't get the gray background)

Could you please put a simple example you're talking about?

Condor
19 Oct 2009, 4:06 AM
Example:

Modify plugin:

cells[i] = ts.gcell.apply({
cls: (typeof group.lastRow == 'boolean' ? group.lastRow : group.header)
? 'ux-grid-hd-group-cell' : 'ux-grid-hd-nogroup-cell',
id: id,

And try the following config:

rows: [
[
{header: 'Very', lastRow: false},
{}
],
[
{header: 'long', lastRow: false},
{header: 'Short header', lastRow: false}
],
[
{header: 'header'},
{lastRow: true}
]
]
...
columns: [
{header: 'Field 1', dataIndex: 'field1'},
{header: 'Field 2', dataIndex: 'field2'}
]

guoj
22 Oct 2009, 7:04 PM
Thanks:)

Maxrunner
28 Oct 2009, 6:23 AM
This is working quite nice except the hd menu. i get the error:

'menu.items is undefined'

Here's my grid:



//Dados
var elemDisp = {
records : [
<c:forEach items="${elements}" varStatus="i" var="elemento">
{
name : '${elemento.titulo}',
descricao: '${elemento.descricao}',
id : ${elemento.id},
chk : '<input type="checkbox" id="${elemento.id}" value="${elemento.titulo}" />'}
<c:if test="${!i.last}">,</c:if>
</c:forEach>
]
};



//Generic fields array to use in defs.
fields = [
{name : 'name', mapping : 'name'},
{name : 'id', mapping : 'id'},
{name : 'descricao', mapping : 'descricao'}
];


// create the data store
GridStore = new Ext.data.JsonStore({
fields : fields,
data : elemDisp,
root : 'records'
});


//selection model based on the checkbox system
CheckboxSM = new Ext.grid.CheckboxSelectionModel({/*header: 'Seleccionar', id: 'test',*/ width: 20});



// Column Model shortcut array
cols =
[
{
id : 'name',
header : headerCol1,
width : 220,
sortable : true,
dataIndex: 'name'},
{
id : 'desc',
header : headerCol2,
width : 280,
sortable : true,
dataIndex: 'descricao'},
{
id : 'desc2',
header : headerCol2,
width : 90,
sortable : true,
dataIndex: 'id'},
CheckboxSM
];

// used to add records to the destination stores
blankRecord = Ext.data.Record.create(fields);

Grid = new Ext.grid.GridPanel({

title : 'Teste',
id : 'panelGrupos',
store : GridStore,
columns : cols,
stripeRows : true,
autoExpandColumn : 'desc',
width : 600,
height : 350,
layout : 'anchor',
region : 'center',
sm : CheckboxSM,
tbar : [
'->',
'-',
seleccionar,
remover
],
bbar : ['->',submeter,fechar,limpar],
viewConfig: {
forceFit: true
},
plugins: [new Ext.ux.plugins.GroupHeaderGrid({
rows: [
[
{header: 'Teste', colspan: 4, align: 'center', dataIndex: 'name'}
],
[

{header: 'Antes', colspan: 2, align: 'center', dataIndex: 'name'},
{header: 'Depois', colspan: 2, align: 'center', dataIndex: 'id'}
]
],
hierarchicalColMenu: true
})]
});

Maxrunner
28 Oct 2009, 8:47 AM
Seems it has to with the hierarchicalColMenu property being set at true.

cginzel
14 Nov 2009, 12:08 PM
I was a little surprised that this control did not support state saving. It turned out to be pretty easy to add. But it seems it me like this is a proper feature to have so I'm sharing my addtions here in red. Also this way maybe I can get some critical review in case I've missed something since I'm still very new to Ext.


Ext.extend(Ext.ux.plugins.GroupHeaderGrid, Ext.util.Observable, {
init: function(grid) {
Ext.applyIf(grid.colModel, this.config);
Ext.apply(grid.getView(), this.viewConfig);

Ext.apply(grid, this.gridConfig);
},

gridConfig: {
applyState : function(state){
this.constructor.prototype.applyState.call(this, state);
this.colModel.rows = state.rows || {};
},

getState : function(){
return Ext.apply(this.constructor.prototype.getState.call(this) || {}, {rows: this.colModel.rows});
}
},

viewConfig: {

Juokelis
4 Mar 2010, 5:33 AM
Seems it has to with the hierarchicalColMenu property being set at true.

I've got same problem and fix it for myself.

Line 201 in GroupHeaderPlugin.js should look like:


var item = (menu.items) ? menu.items.item(gid) : null;

in stead of:


var item = menu.items.item(gid);

And all works with hierarchicalColMenu then.

Maxrunner
4 Mar 2010, 7:07 AM
I get an error when i try to group by this field:

"Uncaught TypeError: Cannot read property 'length' of undefined" at GroupHeadewrPlugin.js line 117."

It still groups but i get the error.

riuslex
9 Mar 2010, 8:00 AM
Hello, i am using a grid with grouped headers, cellSelectionModel and D&D support for dragging Field values from a Form to Grid Cells. I want to create a rule that would not allow the drop of a value if that value already exists somewhere on the column. For normal column headers (without grouping) that was simple. The problem comes when trying to apply this rule for the grouped headers...

Example:

I have as grouped headers:
Monday_| Tuesday_| Wednesday_|_Thursday_|_Friday
and normal column headers:
R11|R12|R21|R22 | R31|R32 |R41|R42 |R51|R52

I managed to build a rule that checks if value being dropped exists on any of the R[xx] columns, but how can i make the rule apply for the Monday/Tuesday/.... headers instead of the R[xx] columns?

This is the current code:



onNodeOver: function(target, dd, e, dragData) {
var t = e.getTarget(this.view.cellSelector);
if (t) {
var rowIndex = this.view.findRowIndex(t);
var columnIndex = this.view.findCellIndex(t);
if ((rowIndex !== false) && (columnIndex !== false)) {
var storeLength = this.store.data.length;
for (i = 0; i < storeLength ; i++) {
var record = this.store.getAt(i);
var columnName = this.grid.getColumnModel().getDataIndex(columnIndex);
// below is the rule i used for columns -> how can i make it for headers?
if (dragData.field && recorda.get(columnName) == dragData.field.getValue()) this.dropOK = false;
}
}
}
return this.dropOK ? this.dropAllowed : this.dropNotAllowed;
},


Another possibility would be to inverse the role of group headers and have them under the columns. That way i would make Monday/Tuesday/.... as columns and R[xx] as headers ... but i have no idea how to make a column that groups headers ...

Maxrunner
10 Mar 2010, 4:04 AM
Anyone using the Ext.grid.GroupingView with this? i get the mentioned error when choosing "group by this field"...

riuslex
10 Mar 2010, 6:27 AM
Hello, i am using a grid with grouped headers, cellSelectionModel and D&D support for dragging Field values from a Form to Grid Cells. I want to create a rule that would not allow the drop of a value if that value already exists somewhere on the column. For normal column headers (without grouping) that was simple. The problem comes when trying to apply this rule for the grouped headers...

Example:

I have as grouped headers:
Monday_| Tuesday_| Wednesday_|_Thursday_|_Friday
and normal column headers:
R11|R12|R21|R22 | R31|R32 |R41|R42 |R51|R52

I managed to build a rule that checks if value being dropped exists on any of the R[xx] columns, but how can i make the rule apply for the Monday/Tuesday/.... headers instead of the R[xx] columns?



I have managed to create the new rule:



maxSpan = 5; // in my case this is sent by the server

var targetHeaderIndex = parseInt((columnIndex - 1) / maxSpan + 1);
var targetHeaderStart = (targetHeaderIndex -1) * maxSpan + 1;
var targetHeaderEnd = targetHeaderIndex*maxSpan ;
var storeLength= this.store.data.length;
for (i = 0; i < storeLength; i++) {
var recorda = this.store.getAt(i);
for (j=targetHeaderStart;j<=targetHeaderEnd;j++) {
var columnIndex = j;
var columnName = this.grid.getColumnModel().getDataIndex(columnIndex);
if (dragData.field && recorda.get(columnName) == dragData.field.getValue()) this.dropOK = false;
}
}

themcme
1 Apr 2010, 8:49 AM
Hi,

First I would like to say thank you for this plugin!

I just have one question, is it possible to make the headers not sortable?
I tried "sortable: false" but that doesn't change anything.

Thanks!

ww20042005
14 Apr 2010, 7:45 PM
I am using the GroupHeaderGrid,but I have a problem that column header is not seen because of much more column and column is not rolled transversely ;

ww20042005
14 Apr 2010, 8:09 PM
The writing of English is very good.I admire you so much.

xbboys
28 Apr 2010, 11:48 PM
Modify grid.getColumnModel().rows and call grid.getView().updateHeaders() (calling grid.getColumnModel().setConfig() will also update the headers).

This has been quite a while but is this thread closed? If so, I will post in a new thread.

Can you please explain more in detail, Condor? It will be great if you could provide some sample code. I need to group 3 quantities with 2 prices for each of them. The quantity header will be base on a jsonstore value retrieved from DB.

qty 1: 1000 || qty 2: 2000 || qty 3 : 3000
Price1 | Price 2 || Price1 | Price 2 || Price1 | Price 2

fcovas
17 Jun 2010, 10:25 AM
I'm trying to set the values of the Group Headers dynamically!

if i try to do something such as:

cm=gridChargesDetails.getColumnModel();
cm.setColumnHeader(3, updatedValue);

It won't work, since it seems that the GroupHeaders cannot be accessed directly through an index;

Setting a dataIndex to the GroupHeader does not modify its "header" value either, such as in:

{header: 'Shipment Type 1', colspan: 2, align: 'center'},

Can you please help me with this matter?

Thank you

Fallen Zen
23 Jul 2010, 1:40 AM
I'm getting an error though the script is "working" in line 117 of GroupHeaderPlugin.js r is undefined. It can be initiated upon clicking "Group By This Field" option.

Here's my column config:


plugins: [new Ext.ux.plugins.GroupHeaderGrid({
rows: [
[
{header: '', colspan: 9, align: 'center'},
{header: 'Lipiec 2010', colspan: 31, align: 'center'}
]
],
hierarchicalColMenu: false
}),
summary,
viewerGridFilters
],
columns : [
{header : 'Projekt', sortable:true, width: 200, menuDisabled: false, dataIndex : 'project', summaryType: 'count', summaryRenderer: totalProjects},
{header : 'Producent', sortable:true, width: 130, menuDisabled: false, dataIndex : 'producer'},
{header : 'Opis', sortable:true, width: 80, menuDisabled: false, dataIndex : 'task'},
{header : 'Data rozpoczęcia', xtype: 'datecolumn', format: 'Y-m-d', sortable:true, width:100, menuDisabled: true, dataIndex : 'start'},
{header : '%', sortable:true, width: 40, menuDisabled: true, dataIndex : 'busy'},
{header : 'Stan', sortable:true, width: 40, menuDisabled: false, dataIndex : 'status', renderer: function (value, meta, record, rowIndex, columnIndex, e) {if (value == true) {return '<div><img src="resources/images/viewer/true.png" /></div>';} else {return '<div><img src="resources/images/viewer/false.png" /></div>';}}},
{header : 'Kwota sugerowana', sortable:true, width: 110, menuDisabled: true, summaryType: 'sum',dataIndex : 'amount1'},
{header : 'Kwota umówiona', sortable:true, width: 100, menuDisabled: true, summaryType: 'sum', dataIndex : 'amount2'},
{header : 'Ilość dni bookingu', sortable:true, width: 50, menuDisabled: true, summaryType: 'sum', dataIndex : 'days'},
{header: '1', dataIndex: 1, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '2', dataIndex: 2, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '3', dataIndex: 3, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '4', dataIndex: 4, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '5', dataIndex: 5, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '6', dataIndex: 6, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '7', dataIndex: 7, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '8', dataIndex: 8, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '9', dataIndex: 9, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '10', dataIndex: 10, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '11', dataIndex: 11, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '12', dataIndex: 12, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '13', dataIndex: 13, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '14', dataIndex: 14, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '15', dataIndex: 15, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '16', dataIndex: 16, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '17', dataIndex: 17, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '18', dataIndex: 18, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '19', dataIndex: 19, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '20', dataIndex: 20, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '21', dataIndex: 21, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '22', dataIndex: 22, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '23', dataIndex: 23, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '24', dataIndex: 24, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '25', dataIndex: 25, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '26', dataIndex: 26, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '27', dataIndex: 27, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '28', dataIndex: 28, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '29', dataIndex: 29, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '30', dataIndex: 30, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})},
{header: '31', dataIndex: 31, width: 25, sortable: false, menuDisabled: true, summaryType: 'sum', editor: new Ext.form.NumberField({allowBlank: true, allowNegative: false, maxValue: 24})}
],

nosferatum
29 Sep 2010, 5:30 AM
Is it possible to mix GroupHeaderGrid and Ext.ux.LiveGrid? It would be very essential combination!

Oh, I've seen the example in your zip-archive, adding GroupHeaderGrid is just adding "plugins" to GridPanel,
I'll try do to it with Ext.ux.grid.livegrid.GridPanel and see the results.

Works ok, but there are some issues for hierarchical menu - ticks in column visibility menu does not change when clicking (the columns hide\change correctly) , but after closing this menu and reopening it values of the ticks are correct.

nosferatum
1 Oct 2010, 4:28 AM
A modification request:
when column is used as sorted, its header style changes to blue color instead of gray (standart ext theme, as in your example). In case of multirow column header, the upper should have also been painted to blue. Just add this style to them. An example you can see on your example page - try to sort table by "Id" column - the lower header part changes to blue, while rowspanned empty header rows stay gray.

superDuke
13 Oct 2010, 5:01 AM
Anyone having issues with this plugin in Ext 3.3.0?

Condor
13 Oct 2010, 5:13 AM
I assume you are using the ColumnGroupHeader plugin from the Ext 3.3.0 examples/ux directory and not this plugin?

superDuke
13 Oct 2010, 5:21 AM
Ofcourse not. I'll take a look, thanks

nosferatum
13 Oct 2010, 6:02 AM
Oh, I didn't look at 3.3.0 yet, still using 3.2.1 in my project. I'll try ColumnGroupHeader plugin, hoping it works better then Ext.ux.plugins.GroupHeaderGrid.

Condor
13 Oct 2010, 6:05 AM
I updated the first post to indicate this.

This plugin is only meant for Ext 3.0. In Ext 3.1+ it is included in the SDK examples/ux folder (renamed to ColumnHeaderGroup).

nosferatum
13 Oct 2010, 6:09 AM
Yes, you're right, and it's already in examples. And multiple sorting too. Nice. I'll try both.

Then it'll be great also to improve and include LiveGrid extension to standard Extjs extensions.

hjones
12 Nov 2010, 10:53 AM
This is a great plugin...I'm using it with hierarchicalColMenu: false.

When the user clicks on the header menu and on the Columns option ALL the checked menu items underneath get checked...with no visible change to the visible columns.
This appears to be a bug? When the user clicks on the Columns header menu item....all child items get checked but not actual column changes.
I've noted that this happens when hierarchicalColMenu: true too.

I fixed this by wrapping the section of code in the default block of function handleHdMenuClick with the following guard condition...



if (id !== 'columns') { /* START FIX */

item.checked = !item.checked;
if(item.menu){
var updateChildren = function(menu){
menu.items.each(function(childItem){
if(!childItem.disabled){
childItem.setChecked(item.checked, false);
if(childItem.menu){
updateChildren(childItem.menu);
}
}
});
}
updateChildren(item.menu);
}
var parentMenu = item, parentItem;
while(parentMenu = parentMenu.parentMenu){
if(!parentMenu.parentMenu || !(parentItem = parentMenu.parentMenu.items.get(parentMenu.getItemId())) || !parentItem.setChecked){
break;
}
var checked = parentMenu.items.findIndexBy(function(m){
return m.checked;
}) >= 0;
parentItem.setChecked(checked, true);
}
item.checked = !item.checked;

} /* END FIX */


This stops the group checking from happening on the default 'Columns' parent menu item in the header menu.

lugreen
19 Nov 2010, 6:10 AM
Hi,thanks your great work;
I want to use ColumnHeaderGroup with RowExpander、CheckboxSelectionModel.
but i find that the group header cant't render correctly,how to use ColumnHeaderGroup with RowExpander、CheckboxSelectionModel?

Condor
19 Nov 2010, 9:52 AM
Hi,thanks your great work;
I want to use ColumnHeaderGroup with RowExpander、CheckboxSelectionModel.
but i find that the group header cant't render correctly,how to use ColumnHeaderGroup with RowExpander、CheckboxSelectionModel?

Those should work together. What exactly is the problem?

lugreen
19 Nov 2010, 5:29 PM
Those should work together. What exactly is the problem?
I simply modified the ColumnHeaderGroup demo for test:



the code is :




...

...

...

var sm = new xg.CheckboxSelectionModel(); //add

var expander = new Ext.ux.grid.RowExpander({
tpl : new Ext.Template(
'<p><b>Company:</b> {ParisProductX}</p><br>'
)
}); // add
columns=[sm,rownum,expander].concat(columns); //add
var grid = new Ext.grid.GridPanel({
renderTo: document.body,
title: 'Sales By Location',
width: 1000,
height: 400,
store: new Ext.data.ArrayStore({
fields: fields,
data: data
}),
columns: columns,
viewConfig: {
forceFit: true
},
plugins:[group,expander]
,sm:sm
});



i taked screenshot :

http://images.cnblogs.com/cnblogs_com/lugreen/ColumnHeaderGroupIssue2.jpg

Condor
20 Nov 2010, 1:58 AM
That's a know css problem of CheckboxSelectionModel.

The easiest way to solve this is to specify a different dataIndex in you columngroups.

araaku
22 Nov 2010, 4:05 AM
Hello Condor,

Thanks!!
It worked like a charm for me!!

Can you please help me in, check/uncheck the parent column as well, when one or more child columns are checked/unchecked.

For eg: Suppose "A" is a group and it has three childs (A1, A2, A3). I want as and when i am checking A1/A2/A3, "A"(parent column) automatically gets checked at same time....


My menu click code is here:


handleHdMenuClick : function(item){
var index = this.hdCtxIndex,
cm = this.cm,
ds = this.ds,
id = item.getItemId();
switch(id){
case 'asc':
ds.sort(cm.getDataIndex(index), 'ASC');
break;
case 'desc':
ds.sort(cm.getDataIndex(index), 'DESC');
break;
default:
if(id.substr(0, 5) == 'group'){
var i = id.split('-'),
row = parseInt(i[1], 10), col = parseInt(i[2], 10),
r = this.cm.rows[row], group, gcol = 0;
for (var i = 0, len = r.length; i < len; i++) {
group = r[i];
if (col >= gcol && col < gcol + group.colspan) {
break;
}
gcol += group.colspan;
}
if(item.checked){
var max = cm.getColumnsBy(this.isHideableColumn, this).length;
for (var i = gcol, len = gcol + group.colspan; i < len; i++){
if(!cm.isHidden(i)){
max--;
}
}
if(max < 1){
this.onDenyColumnHide();
return false;
}
}
for (var i = gcol, len = gcol + group.colspan; i < len; i++){
if(cm.config[i].fixed !== true && cm.config[i].hideable !== false){
cm.setHidden(i, item.checked);
}
}
}
//if menu is not a group //custom
else{
index = cm.getIndexById(id.substr(4));
if(index != -1){
if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){
this.onDenyColumnHide();
return false;
}
cm.setHidden(index, item.checked);
}
}
item.checked = !item.checked; //custom
if(item.menu){
var updateChildren = function(menu){
menu.items.each(function(childItem){
if(!childItem.disabled){
childItem.setChecked(item.checked, true);
if(childItem.menu){
updateChildren(childItem.menu);
}
}
});
}
updateChildren(item.menu);
}


var parentMenu = item, parentItem;
while(parentMenu = parentMenu.parentMenu){
console.log(parentMenu);
if(!parentMenu.parentMenu ||
!(parentItem = parentMenu.parentMenu.items.get(parentMenu.getItemId())) ||
!parentItem.setChecked){
break;
}
var checked = parentMenu.parentMenu.items.findIndexBy(function(m){
return m.checked;
}) >= 0;
parentItem.setChecked(checked, true);
}
item.checked = !item.checked;
}

return true;
},


Looking forward for your response.

Condor
22 Nov 2010, 7:06 AM
So you want to show the group as checked if at least one of it's children is checked (instead of all children - what it is now)?

araaku
29 Nov 2010, 3:44 AM
Hi Condor,
Thanks For you response!!!
if we don’t select the checkbox to the left of the group name, and we select the child checkbox(es) within that group when we come back to the display later, the checkbox to the left of the child is checked on, which (to user) indicates that some of the childs within that group are active.

I want if the group checkbox came on immediately whenever any of the child within the group are checked on and was checked off whenever all of the child within the group are unchecked.

Also
If we check the group checkbox on, and we then go and uncheck all of the child within the group, it never unchecks the group checkbox immediately.

Please find Complete JS code:

Code:


/*
* GroupHeaderGrid version 1.4 for Ext 3
*/
Ext.namespace("Ext.ux.plugins");

if(Ext.isWebKit){
Ext.grid.GridView.prototype.borderWidth = 0;
}

Ext.ux.plugins.GroupHeaderGrid = function(config) {
this.config = config;
};

Ext.extend(Ext.ux.plugins.GroupHeaderGrid, Ext.util.Observable, {
init: function(grid) {
Ext.applyIf(grid.colModel, this.config);
Ext.apply(grid.getView(), this.viewConfig);
},

viewConfig: {
initTemplates: function() {
this.constructor.prototype.initTemplates.apply(this, arguments);
var ts = this.templates || {};
if (!ts.gcell) {
ts.gcell = new Ext.XTemplate(
'<td class="x-grid3-hd x-grid3-gcell x-grid3-td-{id} ux-grid-hd-group-row-{row} {cls}" style="{style}">',
'<div {tooltip} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">',
this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '',
'{value}</div></td>'
);
}
this.templates = ts;
this.hrowRe = new RegExp("ux-grid-hd-group-row-(\\d+)", "");
},

renderHeaders: function() {
var ts = this.templates, headers = [], cm = this.cm, rows = cm.rows, tstyle = 'width:' + this.getTotalWidth() + ';';
for (var row = 0, rlen = rows.length; row < rlen; row++) {
var r = rows[row], cells = [];
for (var i = 0, gcol = 0, len = r.length; i < len; i++) {
var group = r[i];
group.colspan = group.colspan || 1;
var id = this.getColumnId(group.dataIndex ? cm.findColumnIndex(group.dataIndex) : gcol);
var gs = Ext.ux.plugins.GroupHeaderGrid.prototype.getGroupStyle.call(this, group, gcol);
cells[i] = ts.gcell.apply({
cls: group.header ? 'ux-grid-hd-group-cell' : 'ux-grid-hd-nogroup-cell',
id: id,
row: row,
style: 'width:' + gs.width + ';' + (gs.hidden ? 'display:none;' : '') + (group.align ? 'text-align:' + group.align + ';' : ''),
tooltip: group.tooltip ? (Ext.QuickTips.isEnabled() ? 'ext:qtip' : 'title') + '="' + group.tooltip + '"' : '',
istyle: group.align == 'right' ? 'padding-right:16px' : '',
btn: this.grid.enableHdMenu && group.header,
value: group.header || '&nbsp;'
});
gcol += group.colspan;
}
// hide the top header //custom
if(row==0)
{
headers[row] = ts.header.apply({
tstyle: tstyle + 'display: none;' ,
cells: cells.join('')
});
}
else
{
headers[row] = ts.header.apply({
tstyle: tstyle,
cells: cells.join('')
});
}
}
headers.push(this.constructor.prototype.renderHeaders.apply(this, arguments));
return headers.join('');
},

onColumnWidthUpdated : function(){
this.constructor.prototype.onColumnWidthUpdated.apply(this, arguments);
Ext.ux.plugins.GroupHeaderGrid.prototype.updateGroupStyles.call(this);
},

onAllColumnWidthsUpdated : function(){
this.constructor.prototype.onAllColumnWidthsUpdated.apply(this, arguments);
Ext.ux.plugins.GroupHeaderGrid.prototype.updateGroupStyles.call(this);
},

onColumnHiddenUpdated : function(){
this.constructor.prototype.onColumnHiddenUpdated.apply(this, arguments);
Ext.ux.plugins.GroupHeaderGrid.prototype.updateGroupStyles.call(this);
// this.updateGroupWidths();
},



// private
updateGroupWidths : function(){
if(!this.enableGrouping || !this.hasRows()){
return;
}
var tw = Math.max(this.cm.getTotalWidth(), this.el.dom.offsetWidth-this.getScrollOffset()) +'px';
var gs = this.getGroups();
for(var i = 0, len = gs.length; i < len; i++){
gs[i].firstChild.style.width = tw;
}
},
getHeaderCell : function(index){
return this.mainHd.query(this.cellSelector)[index];
},

findHeaderCell : function(el){
return el ? this.fly(el).findParent('td.x-grid3-hd', this.cellSelectorDepth) : false;
},

findHeaderIndex : function(el){
var cell = this.findHeaderCell(el);
return cell ? this.getCellIndex(cell) : false;
},

updateSortIcon : function(col, dir){
var sc = this.sortClasses;
var hds = this.mainHd.select(this.cellSelector).removeClass(sc);
hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);
},

handleHdMenuClick : function(item){
var index = this.hdCtxIndex,
cm = this.cm,
ds = this.ds,
id = item.getItemId();
switch(id){
case 'asc':
ds.sort(cm.getDataIndex(index), 'ASC');
break;
case 'desc':
ds.sort(cm.getDataIndex(index), 'DESC');
break;
default:
if(id.substr(0, 5) == 'group'){
var i = id.split('-'),
row = parseInt(i[1], 10), col = parseInt(i[2], 10),
r = this.cm.rows[row], group, gcol = 0;
for (var i = 0, len = r.length; i < len; i++) {
group = r[i];
if (col >= gcol && col < gcol + group.colspan) {
break;
}
gcol += group.colspan;
}
if(item.checked){
var max = cm.getColumnsBy(this.isHideableColumn, this).length;
for (var i = gcol, len = gcol + group.colspan; i < len; i++){
if(!cm.isHidden(i)){
max--;
}
}
if(max < 1){
this.onDenyColumnHide();
return false;
}
}
for (var i = gcol, len = gcol + group.colspan; i < len; i++){
if(cm.config[i].fixed !== true && cm.config[i].hideable !== false){
cm.setHidden(i, item.checked);
}
}
}

else{
index = cm.getIndexById(id.substr(4));
if(index != -1){
if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){
this.onDenyColumnHide();
return false;
}
cm.setHidden(index, item.checked);
}
}
item.checked = !item.checked; //custom
if(item.menu){
var updateChildren = function(menu){
menu.items.each(function(childItem){
if(!childItem.disabled){
childItem.setChecked(item.checked, true);
if(childItem.menu){
updateChildren(childItem.menu);
}
}
});
}
updateChildren(item.menu);
}
var parentMenu = item, parentItem;
while(parentMenu = parentMenu.parentMenu){
if (typeof console != 'undefined') { //// if condition //custom
console.log(parentMenu);
}

if(!parentMenu.parentMenu ||
!(parentItem = parentMenu.parentMenu.items.get(parentMenu.getItemId())) ||
!parentItem.setChecked){

break;
}
var checked = parentMenu.items.findIndexBy(function(m){
return m.checked;
}) >= 0;
parentItem.setChecked(checked, true);
}
item.checked = !item.checked;
}

return true;
},

// =-----------e
isHideableColumn : function(c){
return !c.hidden && !c.fixed;
},

beforeColMenuShow : function(){
var cm = this.cm, rows = this.cm.rows;
this.colMenu.removeAll();
for(var col = 0, clen = cm.getColumnCount(); col < clen; col++){
var menu = this.colMenu, text = cm.getColumnHeader(col);
if(cm.config[col].fixed !== true && cm.config[col].hideable !== false){
for (var row = 0, rlen = rows.length; row < rlen; row++) {
var r = rows[row], group, gcol = 0;
for (var i = 0, len = r.length; i < len; i++) {
group = r[i];
if (col >= gcol && col < gcol + group.colspan) {
break;
}
gcol += group.colspan;
}
if (group && group.header) {
if (cm.hierarchicalColMenu) {
var gid = 'group-' + row + '-' + gcol;
var item = menu.items.item(gid);
var submenu = item ? item.menu : null;
if (!submenu) {
submenu = new Ext.menu.Menu({itemId: gid+ '-hctx'});
submenu.on("itemclick", this.handleHdMenuClick, this);
var checked = false, disabled = true;
for(var c = gcol, lc = gcol + group.colspan; c < lc; c++){
if(!cm.isHidden(c)){
checked = true;
}
if(cm.config[c].hideable !== false){
disabled = false;
}
}
menu.add({
itemId: gid,
text: group.header,
menu: submenu,
hideOnClick:false,
checked: checked,
disabled: disabled
});
}
menu = submenu;
} else {
text = group.header + ' ' + text;
}
}
}
menu.add(new Ext.menu.CheckItem({
itemId: "col-"+cm.getColumnId(col),
text: text,
checked: !cm.isHidden(col),
hideOnClick:false,
disabled: cm.config[col].hideable === false
}));
}
}
},

renderUI : function(){
this.constructor.prototype.renderUI.apply(this, arguments);
// Ext.apply(this.columnDrop, Ext.ux.plugins.GroupHeaderGrid.prototype.columnDropConfig);
}
},

columnDropConfig : {
getTargetFromEvent : function(e){
var t = Ext.lib.Event.getTarget(e);
return this.view.findHeaderCell(t);
},

positionIndicator : function(h, n, e){
var data = Ext.ux.plugins.GroupHeaderGrid.prototype.getDragDropData.call(this, h, n, e);
if (data === false) {
return false;
}
var px = data.px + this.proxyOffsets[0];
this.proxyTop.setLeftTop(px, data.r.top + this.proxyOffsets[1]);
this.proxyTop.show();
this.proxyBottom.setLeftTop(px, data.r.bottom);
this.proxyBottom.show();
return data.pt;
},



onNodeDrop : function(n, dd, e, data){
var h = data.header;
if(h != n){
var d = Ext.ux.plugins.GroupHeaderGrid.prototype.getDragDropData.call(this, h, n, e);
var row = Ext.ux.plugins.GroupHeaderGrid.prototype.getGroupRowIndex.call(this.view, h);

// if (d === false) { //custom
// return false;
// }

// if (d.comment =="no") { //custom
// return false;
// }
var cm = this.grid.colModel, right = d.oldIndex < d.newIndex, rows = cm.rows;

for (var row = d.row, rlen = rows.length; row < rlen; row++) {
var r = rows[row], len = r.length, fromIx = 0, span = 1, toIx = len;
for (var i = 0, gcol = 0; i < len; i++) {
var group = r[i];

if (d.oldIndex >= gcol && d.oldIndex < gcol + group.colspan) {
fromIx = i;
}
if (d.oldIndex + d.colspan - 1 >= gcol && d.oldIndex + d.colspan - 1 < gcol + group.colspan) {
span = i - fromIx + 1;
}
if (d.newIndex >= gcol && d.newIndex < gcol + group.colspan) {
toIx = i;
}
gcol += group.colspan;
}
var groups = r.splice(fromIx, span);
rows[row] = r.splice(0, toIx - (right ? span : 0)).concat(groups).concat(r);
}
for (var c = 0; c < d.colspan; c++) {
var oldIx = d.oldIndex + (right ? 0 : c), newIx = d.newIndex + (right ? -1 : c);
var newparentGroup = Ext.ux.plugins.GroupHeaderGrid.prototype.getGroupSpan.call(this.view, row - 1, newIx); //custom
var oldparentGroup = Ext.ux.plugins.GroupHeaderGrid.prototype.getGroupSpan.call(this.view, row - 1, oldIx); //custom
var oldGroup = Ext.ux.plugins.GroupHeaderGrid.prototype.getGroupSpan.call(this.view, row, oldIx); //custom
var newGroup = Ext.ux.plugins.GroupHeaderGrid.prototype.getGroupSpan.call(this.view, row, newIx); //custom

//if the fields are of not same group
if(newparentGroup.header!=oldparentGroup.header)
{
return false;
}
//if the fields are of not same group //custom
if(newGroup.colspan!=oldGroup.colspan)
{
return false;
}

//if the fields are of not same group //custom
if(newparentGroup.colspan!=oldparentGroup.colspan)
{
return false;
}
cm.moveColumn(oldIx, newIx);
// this.grid.fireEvent("columnmove", oldIx, newIx); //custom
}
return true;
}
return false;
}
},

getGroupStyle: function(group, gcol) {
var width = 0, hidden = true;
for (var i = gcol, len = gcol + group.colspan; i < len; i++) {
if (!this.cm.isHidden(i)) {
var cw = this.cm.getColumnWidth(i);
if(typeof cw == 'number'){
width += cw;
}
hidden = false;
}
}
return {
width: (Ext.isBorderBox ? width : Math.max(width - this.borderWidth, 0)) + 'px',
hidden: hidden
};
},

updateGroupStyles: function(col) {
var tables = this.mainHd.query('.x-grid3-header-offset > table'), tw = this.getTotalWidth(), rows = this.cm.rows;
for (var row = 0; row < tables.length; row++) {
tables[row].style.width = tw;
if (row < rows.length) {
var cells = tables[row].firstChild.firstChild.childNodes;
for (var i = 0, gcol = 0; i < cells.length; i++) {
var group = rows[row][i];
if ((typeof col != 'number') || (col >= gcol && col < gcol + group.colspan)) {
var gs = Ext.ux.plugins.GroupHeaderGrid.prototype.getGroupStyle.call(this, group, gcol);
cells[i].style.width = gs.width;
cells[i].style.display = gs.hidden ? 'none' : '';
}
gcol += group.colspan;
}
}
}
},

getGroupRowIndex : function(el){
if(el){
var m = el.className.match(this.hrowRe);
if(m && m[1]){
return parseInt(m[1], 10);
}
}
return this.cm.rows.length;
},

getGroupSpan : function(row, col) {

if (row < 0) {
return {col: 0, colspan: this.cm.getColumnCount(), header:''};
}
var ghdr=''; // header return is custom
var r = this.cm.rows[row];
if (r) {
for(var i = 0, gcol = 0, len = r.length; i < len; i++) {
var group = r[i];
ghdr=group.header;
if (col >= gcol && col < gcol + group.colspan) {
return {col: gcol, colspan: group.colspan, header:ghdr};
}
gcol += group.colspan;
}
return {col: gcol, colspan: 0, header:ghdr};
}

return {col: col, colspan: 1, header:ghdr};
},

getDragDropData : function(h, n, e){
if (h.parentNode != n.parentNode) {
return false;
}
var cmt='';//comment to check before node drop
var cm = this.grid.colModel;
var x = Ext.lib.Event.getPageX(e);
var r = Ext.lib.Dom.getRegion(n.firstChild);
var px, pt;
if((r.right - x) <= (r.right-r.left)/2){
px = r.right+this.view.borderWidth;
pt = "after";
}
else{
px = r.left;
pt = "before";
}
var oldIndex = this.view.getCellIndex(h);
var newIndex = this.view.getCellIndex(n);
if(cm.isFixed(newIndex)){
return false;
}
var row = Ext.ux.plugins.GroupHeaderGrid.prototype.getGroupRowIndex.call(this.view, h);
var oldGroup = Ext.ux.plugins.GroupHeaderGrid.prototype.getGroupSpan.call(this.view, row, oldIndex);

var newGroup = Ext.ux.plugins.GroupHeaderGrid.prototype.getGroupSpan.call(this.view, row, newIndex);

oldIndex = oldGroup.col;
newIndex = newGroup.col + (pt == "after" ? newGroup.colspan : 0);
if(newIndex >= oldGroup.col && newIndex <= oldGroup.col + oldGroup.colspan){
return false;
}
var newparentGroup = Ext.ux.plugins.GroupHeaderGrid.prototype.getGroupSpan.call(this.view, row - 1, newIndex); //custom
var oldparentGroup = Ext.ux.plugins.GroupHeaderGrid.prototype.getGroupSpan.call(this.view, row - 1, oldIndex);
if(pt=='before') //custom
{

if(newparentGroup.colspan==oldparentGroup.colspan) //custom
{
if (newIndex < oldparentGroup.col || newIndex > oldparentGroup.col + oldparentGroup.colspan) {
return false;
}
}
}

return {
r: r,
px: px,
pt: pt,
row: row,
oldIndex: oldIndex,
newIndex: newIndex,
colspan: oldGroup.colspan,
comment:cmt
};
}
});


Looking forward for your response.

Condor
29 Nov 2010, 4:01 AM
Doesn't it do that already?

ps. Are you still using Ext 3.0? For Ext 3.1 and up this plugin (renamed to ColumnHeaderGroup) is now part of the SDK examples/ux and any problems you find with it should be reported in the Bugs section.

araaku
30 Nov 2010, 1:15 AM
Thanks Condor!!!
updated to the new version.. it is working now.:)

ws_hgo
6 Dec 2010, 10:12 PM
very nice
thank you

ser
14 Dec 2010, 4:29 AM
ps. Are you still using Ext 3.0? For Ext 3.1 and up this plugin (renamed to ColumnHeaderGroup) is now part of the SDK examples/ux and any problems you find with it should be reported in the Bugs section.


if I use a plugin GroupHeaderGrid 1.4 the result is "Screenshot-1060.png"
if using ColumnHeaderGroup from ExtJS 3.2.1
the result is "Screenshot-1059.png"
Old plugin works better new.

my code is


var row5 = [
{header: ' ', colspan: 7, align: 'center'},
{header: 'header1', align: 'center'},
{header: 10, colspan: 3, align: 'center'}
];

var row6 = [
{header: '', colspan: 7, align: 'center'},
{header: 'Vid PPO', align: 'center'},
{header: 'asdf', colspan: 3, align: 'center'}
];

//new Ext.ux.plugins.GroupHeaderGrid({
//new Ext.ux.grid.ColumnHeaderGroup
var group = new Ext.ux.plugins.GroupHeaderGrid({
rows: [row6, row5]
});

Condor
14 Dec 2010, 4:38 AM
Yes, they removed some stuff when moving from GroupHeaderGrid to ColumnHeaderGroup, but it's quite simple to write a css rule to make ColumnHeaderGroup display the same as GroupHeaderGrid.

alimmohammad
27 Jan 2011, 1:10 AM
I have a grid using ColumnHeaderGroup plugin. I need to get the name of the top column header. Please help.

ser
27 Jan 2011, 1:20 AM
I did so
from ColumnHeaderGroup


this.group.config.rows[5][2 + offset].header = total - obj.originalValue + parseInt(obj.value);

from general ColumnHeader


this.columns[obj.column].header = total - obj.originalValue + parseInt(obj.value);

and then


grid.getView().updateHeaders();

ser
3 Feb 2011, 7:53 AM
after grid.getView().updateHeaders() i get
Cannot read property 'firstChild' of undefined
In general, because of what this error?

Condor
3 Feb 2011, 8:04 AM
You can only call updateHeaders after the grid view has rendered.

if (grid.getView().innerHd) {
grid.getView().updateHeaders();
}

ser
4 Feb 2011, 12:24 AM
Now i get it. Thanks.

By the way, do not tell me, this table should be created with this plugin, or i can create with the usual headers and then add rows yourself and fill it out and call updateHeaders. I just have to update GroupHeader only if the table is initialized with this plugin

rmesser
14 Feb 2011, 3:39 PM
I'm using the ColumnHeaderGroup ux as provided in the 3.3.1 examples directory, and generally speaking it works very well. However, in my app I need to detect when a user clicks one of these group headers, and I've noticed a problem in that clicking the headers doesn't trigger either a "click" or a "headerclick" event.

To see what I mean you can start with the example at examples/grid/ColumnHeaderGroup.html. Then edit column-header-group.js to add the following listeners to the grid config (right after the "plugins: group" line):



listeners: {
click: function() {console.log('got click', arguments)},
headerclick: function() {console.log('got headerclick', arguments)}
}


Clicking around in the grid triggers the click event as expected, and clicking the bottom row of headers also triggers the headerclick event. But clicking the group headers does nothing at all.

Any ideas on why this is or how to make it trigger the expected events?

ser
14 Feb 2011, 11:36 PM
i'm using
GroupHeaderPlugin-1.4.zip (http://www.sencha.com/forum/attachment.php?attachmentid=15377&d=1249138018) from this topic(first page) and everything works fine

rmesser
15 Feb 2011, 10:09 AM
@ser, what version of ext are you using? I get an error using the GroupHeaderPlugin-1.4.zip files with extjs 3.3.1, although I haven't investigated much.

@Condor -- isn't the ColumnGroupHeader.js plug found in the examples/grid directory now the latest and greatest version? That's what I'm using when I get the problem with click and headerclick events.

ser
15 Feb 2011, 11:11 AM
3.2.1

Condor
16 Feb 2011, 12:31 AM
For Ext 3.1 and up you should be using the version from the ext/examples/ux directory.

Ant1105
28 Mar 2011, 5:46 PM
Condor, any plans for an ExtJS 4 release of this plugin?

ExTriqui
4 Apr 2011, 7:47 AM
I cannot make it work with GroupingView.
The column widths get messed up if you use a GroupingView with this plugin and GroupSummary or GridSummary and start to hide/show columns, cause the events chain is not called in the right order.
I think it's a problem with the events being added as prototype functions in the viewConfig instead of using view.afterMethod("onColumn...") or something like that. I'm not an expert, so if someone could help me fix this I would really appreciate it.

ExTriqui
5 Apr 2011, 11:49 PM
In order to let classes inheriting from GridView update column widths properly, you have to avoid overriding some of its methods:

Ext.ux.grid.ColumnHeaderGroup.override({
init: function(grid){
Ext.applyIf(grid.colModel, this.config);
Ext.apply(grid.getView(), this.viewConfig);
var v = grid.getView();
v.afterMethod('onColumnWidthUpdated', this.updateGroupStyles, v);
v.afterMethod('onAllColumnWidthsUpdated', this.updateGroupStyles, v);
v.afterMethod('onColumnHiddenUpdated', this.updateGroupStyles, v);
},

viewConfig: {
initTemplates: function(){
this.constructor.prototype.initTemplates.apply(this, arguments);
var ts = this.templates || {};
if(!ts.gcell){
ts.gcell = new Ext.XTemplate('<td class="x-grid3-hd x-grid3-gcell x-grid3-td-{id} ux-grid-hd-group-row-{row} {cls}" style="{style}">', '<div {tooltip} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">', this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '', '{value}</div></td>');
}
this.templates = ts;
this.hrowRe = new RegExp("ux-grid-hd-group-row-(\\d+)", "");
},

renderHeaders: function(){
var ts = this.templates, headers = [], cm = this.cm, rows = cm.rows, tstyle = 'width:' + this.getTotalWidth() + ';';

for(var row = 0, rlen = rows.length; row < rlen; row++){
var r = rows[row], cells = [];
for(var i = 0, gcol = 0, len = r.length; i < len; i++){
var group = r[i];
group.colspan = group.colspan || 1;
var id = this.getColumnId(group.dataIndex ? cm.findColumnIndex(group.dataIndex) : gcol), gs = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupStyle.call(this, group, gcol);
cells[i] = ts.gcell.apply({
cls: 'ux-grid-hd-group-cell',
id: id,
row: row,
style: 'width:' + gs.width + ';' + (gs.hidden ? 'display:none;' : '') + (group.align ? 'text-align:' + group.align + ';' : ''),
tooltip: group.tooltip ? (Ext.QuickTips.isEnabled() ? 'ext:qtip' : 'title') + '="' + group.tooltip + '"' : '',
istyle: group.align == 'right' ? 'padding-right:16px' : '',
btn: this.grid.enableHdMenu && group.header,
value: group.header || '&nbsp;'
});
gcol += group.colspan;
}
headers[row] = ts.header.apply({
tstyle: tstyle,
cells: cells.join('')
});
}
headers.push(this.constructor.prototype.renderHeaders.apply(this, arguments));
return headers.join('');
},
/*
onColumnWidthUpdated: function(){
this.constructor.prototype.onColumnWidthUpdated.apply(this, arguments);
Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);
},

onAllColumnWidthsUpdated: function(){
this.constructor.prototype.onAllColumnWidthsUpdated.apply(this, arguments);
Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);
},

onColumnHiddenUpdated: function(){
this.constructor.prototype.onColumnHiddenUpdated.apply(this, arguments);
Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);
},
*/
getHeaderCell: function(index){
return this.mainHd.query(this.cellSelector)[index];
},

findHeaderCell: function(el){
return el ? this.fly(el).findParent('td.x-grid3-hd', this.cellSelectorDepth) : false;
},

findHeaderIndex: function(el){
var cell = this.findHeaderCell(el);
return cell ? this.getCellIndex(cell) : false;
},

updateSortIcon: function(col, dir){
var sc = this.sortClasses, hds = this.mainHd.select(this.cellSelector).removeClass(sc);
hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);
},

handleHdDown: function(e, t){
var el = Ext.get(t);
if(el.hasClass('x-grid3-hd-btn')){
e.stopEvent();
var hd = this.findHeaderCell(t);
Ext.fly(hd).addClass('x-grid3-hd-menu-open');
var index = this.getCellIndex(hd);
this.hdCtxIndex = index;
var ms = this.hmenu.items, cm = this.cm;
ms.get('asc').setDisabled(!cm.isSortable(index));
ms.get('desc').setDisabled(!cm.isSortable(index));
this.hmenu.on('hide', function(){
Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
}, this, {
single: true
});
this.hmenu.show(t, 'tl-bl?');
}else if(el.hasClass('ux-grid-hd-group-cell') || Ext.fly(t).up('.ux-grid-hd-group-cell')){
e.stopEvent();
}
},

handleHdMove: function(e, t){
var hd = this.findHeaderCell(this.activeHdRef);
if(hd && !this.headersDisabled && !Ext.fly(hd).hasClass('ux-grid-hd-group-cell')){
var hw = this.splitHandleWidth || 5, r = this.activeHdRegion, x = e.getPageX(), ss = hd.style, cur = '';
if(this.grid.enableColumnResize !== false){
if(x - r.left <= hw && this.cm.isResizable(this.activeHdIndex - 1)){
cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'e-resize' : 'col-resize'; // col-resize
// not
// always
// supported
}else if(r.right - x <= (!this.activeHdBtn ? hw : 2) && this.cm.isResizable(this.activeHdIndex)){
cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'w-resize' : 'col-resize';
}
}
ss.cursor = cur;
}
},

handleHdOver: function(e, t){
var hd = this.findHeaderCell(t);
if(hd && !this.headersDisabled){
this.activeHdRef = t;
this.activeHdIndex = this.getCellIndex(hd);
var fly = this.fly(hd);
this.activeHdRegion = fly.getRegion();
if(!(this.cm.isMenuDisabled(this.activeHdIndex) || fly.hasClass('ux-grid-hd-group-cell'))){
fly.addClass('x-grid3-hd-over');
this.activeHdBtn = fly.child('.x-grid3-hd-btn');
if(this.activeHdBtn){
this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight - 1) + 'px';
}
}
}
},

handleHdOut: function(e, t){
var hd = this.findHeaderCell(t);
if(hd && (!Ext.isIE || !e.within(hd, true))){
this.activeHdRef = null;
this.fly(hd).removeClass('x-grid3-hd-over');
hd.style.cursor = '';
}
},

handleHdMenuClick: function(item){
var index = this.hdCtxIndex, cm = this.cm, ds = this.ds, id = item.getItemId();
switch(id){
case 'asc':
ds.sort(cm.getDataIndex(index), 'ASC');
break;
case 'desc':
ds.sort(cm.getDataIndex(index), 'DESC');
break;
default:
if(id.substr(0, 6) == 'group-'){
var i = id.split('-'), row = parseInt(i[1], 10), col = parseInt(i[2], 10), r = this.cm.rows[row], group, gcol = 0;
for(var i = 0, len = r.length; i < len; i++){
group = r[i];
if(col >= gcol && col < gcol + group.colspan){
break;
}
gcol += group.colspan;
}
if(item.checked){
var max = cm.getColumnsBy(this.isHideableColumn, this).length;
for(var i = gcol, len = gcol + group.colspan; i < len; i++){
if(!cm.isHidden(i)){
max--;
}
}
if(max < 1){
this.onDenyColumnHide();
return false;
}
}
for(var i = gcol, len = gcol + group.colspan; i < len; i++){
if(cm.config[i].fixed !== true && cm.config[i].hideable !== false){
cm.setHidden(i, item.checked);
}
}
}else if(id.substr(0, 4) == 'col-'){
index = cm.getIndexById(id.substr(4));
if(index != -1){
if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){
this.onDenyColumnHide();
return false;
}
cm.setHidden(index, item.checked);
}
}
if(id.substr(0, 6) == 'group-' || id.substr(0, 4) == 'col-'){
item.checked = !item.checked;
if(item.menu){
var updateChildren = function(menu){
menu.items.each(function(childItem){
if(!childItem.disabled){
childItem.setChecked(item.checked, false);
if(childItem.menu){
updateChildren(childItem.menu);
}
}
});
};
updateChildren(item.menu);
}
var parentMenu = item, parentItem;
while(parentMenu = parentMenu.parentMenu){
if(!parentMenu.parentMenu || !(parentItem = parentMenu.parentMenu.items.get(parentMenu.getItemId())) || !parentItem.setChecked){
break;
}
var checked = parentMenu.items.findIndexBy(function(m){
return m.checked;
}) >= 0;
parentItem.setChecked(checked, true);
}
item.checked = !item.checked;
}
}
return true;
},

beforeColMenuShow: function(){
var cm = this.cm, rows = this.cm.rows;
this.colMenu.removeAll();
for(var col = 0, clen = cm.getColumnCount(); col < clen; col++){
var menu = this.colMenu, title = cm.getColumnHeader(col), text = [];
if(cm.config[col].fixed !== true && cm.config[col].hideable !== false){
for(var row = 0, rlen = rows.length; row < rlen; row++){
var r = rows[row], group, gcol = 0;
for(var i = 0, len = r.length; i < len; i++){
group = r[i];
if(col >= gcol && col < gcol + group.colspan){
break;
}
gcol += group.colspan;
}
if(group && group.header){
if(cm.hierarchicalColMenu){
var gid = 'group-' + row + '-' + gcol,
item = menu.items ? menu.getComponent(gid) : null,
submenu = item ? item.menu : null;
if(!submenu){
submenu = new Ext.menu.Menu({
itemId: gid
});
submenu.on("itemclick", this.handleHdMenuClick, this);
var checked = false, disabled = true;
for(var c = gcol, lc = gcol + group.colspan; c < lc; c++){
if(!cm.isHidden(c)){
checked = true;
}
if(cm.config[c].hideable !== false){
disabled = false;
}
}
menu.add({
itemId: gid,
text: group.header,
menu: submenu,
hideOnClick: false,
checked: checked,
disabled: disabled
});
}
menu = submenu;
}else{
text.push(group.header);
}
}
}
text.push(title);
menu.add(new Ext.menu.CheckItem({
itemId: "col-" + cm.getColumnId(col),
text: text.join(' '),
checked: !cm.isHidden(col),
hideOnClick: false,
disabled: cm.config[col].hideable === false
}));
}
}
},

afterRenderUI: function(){
this.constructor.prototype.afterRenderUI.apply(this, arguments);
Ext.apply(this.columnDrop, Ext.ux.grid.ColumnHeaderGroup.prototype.columnDropConfig);
Ext.apply(this.splitZone, Ext.ux.grid.ColumnHeaderGroup.prototype.splitZoneConfig);
}
}
});

rguerette
30 Apr 2011, 6:22 AM
I'll re-ask a previous question..
"Condor, any plans for an ExtJS 4 release of this plugin?"

What I really need to know after looking at the examples for ExtJS 4 is, is it possible to combine the features from the Locking Grid Column example and the Grouped Header Grid example? If so, I can probably figure it out.

dongdong246
20 May 2011, 10:54 PM
you plugins is have problem!!!!
is'not run

gustavo21
21 May 2011, 5:52 PM
Hello, i set it up in a few minutes and is working great!! im new in extjs, but this plugin is just easy to use. I got my store from sql server 2008 and is really fast!

Thanks!!http://www.sencha.com/forum/images/icons/icon12.png

krishna62
11 Jul 2011, 4:57 AM
Hi Condor,

I have below issue with the ColumnHeaderGroup and new Ext.grid.CheckboxSelectionModel together. The result of the screen as something like below.

http://images.cnblogs.com/cnblogs_com/lugreen/ColumnHeaderGroupIssue2.jpg

I changed the dataIndex of the column group header different as you suggest but I could not see anything. Can you please let me know if there thing wrong I am doing here.

Below is the code that I am adding the dataIndex.

grdTopHeader.push({ header: '', dataIndex:'Test1',align: 'center', colspan:4}); //
grdTopHeader.push({ header: $.i18n._('Version1'),dataIndex:'Test2', align: 'center', colspan: 10});
grdTopHeader.push({ header: $.i18n._('Version2'),dataIndex:'Test3', align: 'center',colspan: 6});

Thanks,
Krishna

softm
8 Oct 2011, 11:40 AM
Is there some easy way to migrate EditorGridPanel - to have grouped look? As ex. have grid with 7 columns.


i add ext-3.3.1/plugins/GroupHeaderPlugin.js, as <script ...

/* * GroupHeaderGrid version 1.4 for Ext 3 */
add css 2 lines, and correct img/url path
i app to EditorGridPanel definition this:



grid = new Ext.grid.EditorGridPanel({

store: store,
cm: cm,
renderTo: 'grid',
width: 958,
height: 500,
frame: true,
loadMask: true,
clicksToEdit: 1,

// 1'st try add G-H-G
viewConfig: {
forceFit: true
},


plugins: [new Ext.ux.plugins.GroupHeaderGrid({
rows: [
[
{},
{header: 'Min', colspan: 3, align: 'center'},
{header: 'Max', colspan: 3, align: 'center'}
]
],
hierarchicalColMenu: true
})],





After this i got: "undefined" in place of grid and JS error:

el is null

chrome://firebug/content/blank.gif mainWrap = new Element(el.child('div.x-grid3-viewport')),


in ext-all-debug.js at 46216 ...

I try debug & no-debug versions of ext. When i remove 2'd red code, i.e. plugins definitions - all start working well.
What am doing wrong? Pls help me ...

brkz
1 Feb 2012, 9:44 AM
I'll re-ask a previous question..
"Condor, any plans for an ExtJS 4 release of this plugin?"

What I really need to know after looking at the examples for ExtJS 4 is, is it possible to combine the features from the Locking Grid Column example and the Grouped Header Grid example? If so, I can probably figure it out.

I second that. Condor, are you still interested in adding Locking grid feature for money?

Tanmaya
24 Feb 2012, 6:37 AM
Hi,I have tried to use the following approach as discussed in this forum,
colModel.rows = ds.meta.rows;colModel.setConfig(ds.columns);The same is not working for me i have tried to evoke it from both the viewConfig onDataChange and the stores load listener, but in both cases my grid is only loading with the normal column structure and the header columns are not being foundmy js code is :
var overViewGridStore = new Ext.data.JsonStore({ fields : [], url : overViewGridStoreUrl, autoLoad : true, listeners : { load : function() { //Ext.getCmp('overViewGrid').getColumnModel().rows = this.reader.jsonData.metaData.headers;; //Ext.getCmp('overViewGrid').getColumnModel().setConfig(this.reader.jsonData.columns); alert(Ext.getCmp('overViewGrid').getColumnModel().rows); }, exception : function() { Ext.MessageBox.show({ icon : Ext.MessageBox.ERROR, buttons : Ext.MessageBox.OK, msg : "Please contact the administrator.", title : 'Error' }); } } }); var group = new Ext.ux.grid.ColumnHeaderGroup({ rows : [] }); var overViewGrid = new Ext.grid.GridPanel({ title : 'grid', anchor : '100% 100%', id : 'overViewGrid', store : overViewGridStore, autoScroll : true, columns : [], viewConfig:{ forceFit : true, onDataChange: function (){ // this.cm.rows = this.ds.reader.jsonData.metaData.headers; // this.cm.setConfig(this.ds.reader.jsonData.columns); } } plugins : group });and my JSON data from server is of the form
{ "metaData": { "headers": [ { "align": "center", "colspan": "1" }, { "align": "center", "header": "xxxxx", "colspan": "5" } ], "idProperty": "transaction", "root": "root", "fields": [ { "mapping": "transaction", "name": "transaction" }, { "mapping": "time1", "name": "time1" }, { "mapping": "count1", "name": "count1" }, { "mapping": "rt1", "name": "rt1" }, { "mapping": "xcount1", "name": "xcount1" }, { "mapping": "xrt1", "name": "xrt1" } ] }, "root": [], "columns": [ { "align": "center", "header": "Transactions", "dataIndex": "transaction", "sortable": "true", "hideable": "false", "id": "transaction" }, { "align": "left", "header": "Time", "dataIndex": "time1", "sortable": "true", "hideable": "false", "id": "time1" }, { "align": "left", "header": "Count", "dataIndex": "count1", "sortable": "true", "hideable": "false", "id": "count1" }, { "align": "left", "header": "Response Time", "dataIndex": "rt1", "sortable": "true", "hideable": "false", "id": "rt1" }, { "align": "left", "header": "Failed Count", "dataIndex": "xcount1", "sortable": "true", "hideable": "false", "id": "xcount1" }, { "align": "left", "header": "Failed Response Time", "dataIndex": "xrt1", "sortable": "true", "hideable": "false", "id": "xrt1" } ]}I am able to render the following32111

sabhandari
21 Jun 2012, 12:08 AM
It doesn't work for me. The error I get is:

Ext.ux.grid is undefined

xiexiest
20 Nov 2012, 9:10 PM
I want to the groupHeader be strong ,and the font-size be bigger .
how to add the css?
Can you help me ?


plugins: [new Ext.ux.plugins.GroupHeaderGrid({
rows: [
[
{header: 'HostInfo, colspan: 13, align: 'center'},
{header: 'In', colspan: 9, align: 'center'},
{header: 'Out', colspan: 9, align: 'center'},
{header: 'Others', colspan: 2, align: 'center'}
]
],
hierarchicalColMenu: true
})],

babyhhcsy
10 Jul 2013, 8:31 PM
我做了测试,很好=P~

babyhhcsy
10 Jul 2013, 8:34 PM
很好