PDA

View Full Version : Extending the RowExpander Plugin



CutterBl
17 Dec 2007, 4:10 AM
I've just put up a post on my blog about Extending the RowExpander Plugin (http://tinyurl.com/2r3mnw), complete with the source and a link to the example page (http://examples.cutterscrossing.com/rowexpander). Basically, I wanted the ability to pass a new argument into the rowexpander, a function reference to be called on expansion. I wanted the ability to populate the expanded area, at the time of expansion, with an Ajax call. Afterwards I showed it to Rey [Bango], who said "get an example up and get it on the forums." So here I am.

You can get the full source of the example from the post, but here's the plugin itself:



/*
* Ext JS Library 2.0
* Copyright(c) 2006-2007, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*
* MODIFIED: SGB [12.12.07]
* Added support for a new config option, remoteDataMethod,
* including getter and setter functions, and minor mods
* to the beforeExpand and expandRow functions
*/

Ext.grid.RowExpander = function(config){
Ext.apply(this, config);
Ext.grid.RowExpander.superclass.constructor.call(this);

if(this.tpl){
if(typeof this.tpl == 'string'){
this.tpl = new Ext.Template(this.tpl);
}
this.tpl.compile();
}

this.state = {};
this.bodyContent = {};

this.addEvents({
beforeexpand : true,
expand: true,
beforecollapse: true,
collapse: true
});
};

Ext.extend(Ext.grid.RowExpander, Ext.util.Observable, {
header: "",
width: 20,
sortable: false,
fixed:true,
dataIndex: '',
id: 'expander',
lazyRender : true,
enableCaching: true,

getRowClass : function(record, rowIndex, p, ds){
p.cols = p.cols-1;
var content = this.bodyContent[record.id];
if(!content && !this.lazyRender){
content = this.getBodyContent(record, rowIndex);
}
if(content){
p.body = content;
}
return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';
},

init : function(grid){
this.grid = grid;

var view = grid.getView();
view.getRowClass = this.getRowClass.createDelegate(this);

view.enableRowBody = true;

grid.on('render', function(){
view.mainBody.on('mousedown', this.onMouseDown, this);
}, this);
},

getBodyContent : function(record, index){
if(!this.enableCaching){
return this.tpl.apply(record.data);
}
var content = this.bodyContent[record.id];
if(!content){
content = this.tpl.apply(record.data);
this.bodyContent[record.id] = content;
}
return content;
},
// Setter and Getter methods for the remoteDataMethod property
setRemoteDataMethod : function (fn){
this.remoteDataMethod = fn;
},

getRemoteDataMethod : function (record, index){
if(!this.remoteDataMethod){
return;
}
return this.remoteDataMethod.call(this,record,index);
},

onMouseDown : function(e, t){
if(t.className == 'x-grid3-row-expander'){
e.stopEvent();
var row = e.getTarget('.x-grid3-row');
this.toggleRow(row);
}
},

renderer : function(v, p, record){
p.cellAttr = 'rowspan="2"';
return '<div class="x-grid3-row-expander">&#160;</div>';
},

beforeExpand : function(record, body, rowIndex){
if(this.fireEvent('beforexpand', this, record, body, rowIndex) !== false){
// If remoteDataMethod is defined then we'll need a div, with a unique ID,
// to place the content
if(this.remoteDataMethod){
this.tpl = new Ext.Template("<div id='remData" + rowIndex + "' class='rem-data-expand'><\div>");
}
if(this.tpl && this.lazyRender){
body.innerHTML = this.getBodyContent(record, rowIndex);
}

return true;
}else{
return false;
}
},

toggleRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);
},

expandRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
var record = this.grid.store.getAt(row.rowIndex);
var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
if(this.beforeExpand(record, body, row.rowIndex)){
this.state[record.id] = true;
Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');
if(this.fireEvent('expand', this, record, body, row.rowIndex) !== false){
// If the expand event is successful then get the remoteDataMethod
this.getRemoteDataMethod(record,row.rowIndex);
}
}
},

collapseRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
var record = this.grid.store.getAt(row.rowIndex);
var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);
if(this.fireEvent('beforcollapse', this, record, body, row.rowIndex) !== false){
this.state[record.id] = false;
Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
this.fireEvent('collapse', this, record, body, row.rowIndex);
}
}
});


After that all you need is a function, and the config to point to it:



// FUNCTION: getMyStuff
// @param record : actual record in grid row
// @param index : row index calling the function
function getMyStuff(record,index){
// Using JQuery to 'load' the expanded row with content pulled remotely
$('#remData'+index).load('tester.html');
};

var expander = new xg.RowExpander({
remoteDataMethod : getMyStuff
});

rkrishna_1975
17 Dec 2007, 6:52 AM
I have to test this out... but wow... This is very similar to what I had in mind. I may still want to have a container (so that I can load what ever I want there, like say a remote component or another grid etc..) in the expanded section but this is the right direct for it.

CutterBl
17 Dec 2007, 7:36 AM
Thanks Ramki. Actually the call to the remote method will automatically create a container object in the expanded area. There's more info in the blog posting.

rkrishna_1975
17 Dec 2007, 9:08 AM
Thanks Ramki. Actually the call to the remote method will automatically create a container object in the expanded area. There's more info in the blog posting.

Wow.... That is great. I will play with this sometime today.

jack
21 Dec 2007, 4:47 AM
I'm trying to use the row expander on my code but its not working..
Edit: Nevermind, solved the problem and its working now..

http://www.ufu.br/ramais/

I'm getting data remotely from a database (ramais.php), so now i need to find out how to get this data remotely and show it on the grid (ramais.js).

Iveco
21 Dec 2007, 11:33 AM
Thank you very much Cutter for this plugin,
but I am in need of some more functionality and right now I don't know how to do this. (Never coded a plugin yet, but the rowExpander is that nice I really want to have it)

Two things are missing:

1) I attached a 'celldblclick' event to the grid, thought when the row gets expanded, the visible expanded area doesn't have this event, how can I also attach the celldblclick event to the rowExpander's area for that row?

2) When I have a row expanded and showing the rowExpander, meanwhile updating the grid's datastore (though refreshing the grid), it will show all rows collapsed, even if some of them before the update expanded but the ICON's doesn't get changed. I tried adding an event to the datastore, which then will check all rowExpander.state for that row and replaceClass the Icon but the problem is state only stores the id of the row not the rowIndex itself.



ds.reload( // reload the datastore
{
callback : function(r, options, success)
{
if(success)
{
var expander = XXX.getExpander();
// find all collapsed RowExpander because they won't reset them theirself when the Grid/Store get's updated
if(expander.state)
{
for(row in expander.state)
{
if(expander.state[row]) // when it was collapsed while updating, it will still show the wrong icon, fix this
{
expander.state[row] = false;
// NEED ROWINDEX HERE NOT ROW!
Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
}
}
}
}



Please someone help me!

Regards,

Iveco

meteorbites
2 Jan 2008, 11:21 PM
I find this more convenient


Ext.get('remData'+index).load({
url: "tester.html",
scripts: true,
params: "param1=foo&param2=bar",
text: "Loading Detailed Information..."
});


than


$('#remData'+index).load('tester.html');


Great plugin.. ;)

thanks

Iveco
4 Jan 2008, 9:01 AM
1) I attached a 'celldblclick' event to the grid, thought when the row gets expanded, the visible expanded area doesn't have this event, how can I also attach the celldblclick event to the rowExpander's area for that row?


Got this fixed by using "rowdblclick".



2) When I have a row expanded and showing the rowExpander, meanwhile updating the grid's datastore (though refreshing the grid), it will show all rows collapsed, even if some of them before the update expanded but the ICON's doesn't get changed. I tried adding an event to the datastore, which then will check all rowExpander.state for that row and replaceClass the Icon but the problem is state only stores the id of the row not the rowIndex itself.


Got this fixed by using:



Ext.select('div.x-grid3-row-expanded').replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');


(dont forget to reset the state's with the for loop I posted more top)

Regards,

Iveco

nak1
25 Mar 2008, 8:31 PM
Hi CutterBl, I was wonder if you could give me a hand on a dilemma I'm having using RowExpander. I want to render components that I've executed multiple times. I know you can't render a component multiple times, but it seems like a waste to rebuild each time someone clicks the expander on my grid. Here's my forum post; http://extjs.com/forum/showthread.php?t=30422&highlight=RowExpander+multiple

Based on your post, I'm thought you'd know the answer. BTW I'm a CF dev guy myself.

N

a274688
1 May 2008, 12:22 PM
Hi CutterBl

Thanks for the great plugin ... works like a charm ... saved me hours of work.

wm003
2 May 2008, 5:51 AM
Great plugin, thanks for sharing!

NoahK17
2 May 2008, 11:49 AM
If you are finding this Plugin does not play well with Animal's ExportGridToExcel plugin, please see this thread on my update to his plugin to work properly with this plugin. http://extjs.com/forum/showthread.php?t=32400&page=4

For anyone else who might be confused on how to get this plugin to work properly, follow these directions.

Create a new file and name it GridRowExpander.js, put the following code inside and save it.

/*
* Ext JS Library 2.0
* Copyright(c) 2006-2007, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*
* MODIFIED: SGB [12.12.07]
* Added support for a new config option, remoteDataMethod,
* including getter and setter functions, and minor mods
* to the beforeExpand and expandRow functions
*/

Ext.grid.RowExpander = function(config){
Ext.apply(this, config);
Ext.grid.RowExpander.superclass.constructor.call(this);

if(this.tpl){
if(typeof this.tpl == 'string'){
this.tpl = new Ext.Template(this.tpl);
}
this.tpl.compile();
}

this.state = {};
this.bodyContent = {};

this.addEvents({
beforeexpand : true,
expand: true,
beforecollapse: true,
collapse: true
});
};

Ext.extend(Ext.grid.RowExpander, Ext.util.Observable, {
header: "",
width: 20,
sortable: false,
fixed:true,
dataIndex: '',
id: 'expander',
lazyRender : true,
enableCaching: true,

getRowClass : function(record, rowIndex, p, ds){
p.cols = p.cols-1;
var content = this.bodyContent[record.id];
if(!content && !this.lazyRender){
content = this.getBodyContent(record, rowIndex);
}
if(content){
p.body = content;
}
return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';
},

init : function(grid){
this.grid = grid;

var view = grid.getView();
view.getRowClass = this.getRowClass.createDelegate(this);

view.enableRowBody = true;

grid.on('render', function(){
view.mainBody.on('mousedown', this.onMouseDown, this);
}, this);
},

getBodyContent : function(record, index){
if(!this.enableCaching){
return this.tpl.apply(record.data);
}
var content = this.bodyContent[record.id];
if(!content){
content = this.tpl.apply(record.data);
this.bodyContent[record.id] = content;
}
return content;
},
// Setter and Getter methods for the remoteDataMethod property
setRemoteDataMethod : function (fn){
this.remoteDataMethod = fn;
},

getRemoteDataMethod : function (record, index){
if(!this.remoteDataMethod){
return;
}
return this.remoteDataMethod.call(this,record,index);
},

onMouseDown : function(e, t){
if(t.className == 'x-grid3-row-expander'){
e.stopEvent();
var row = e.getTarget('.x-grid3-row');
this.toggleRow(row);
}
},

renderer : function(v, p, record){
p.cellAttr = 'rowspan="2"';
return '<div class="x-grid3-row-expander"> </div>';
},

beforeExpand : function(record, body, rowIndex){
if(this.fireEvent('beforexpand', this, record, body, rowIndex) !== false){
// If remoteDataMethod is defined then we'll need a div, with a unique ID,
// to place the content
if(this.remoteDataMethod){
this.tpl = new Ext.Template("<div id='remData" + rowIndex + "' class='rem-data-expand'><\div>");
}
if(this.tpl && this.lazyRender){
body.innerHTML = this.getBodyContent(record, rowIndex);
}

return true;
}else{
return false;
}
},

toggleRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);
},

expandRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
var record = this.grid.store.getAt(row.rowIndex);
var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
if(this.beforeExpand(record, body, row.rowIndex)){
this.state[record.id] = true;
Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');
if(this.fireEvent('expand', this, record, body, row.rowIndex) !== false){
// If the expand event is successful then get the remoteDataMethod
this.getRemoteDataMethod(record,row.rowIndex);
}
}
},

collapseRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
var record = this.grid.store.getAt(row.rowIndex);
var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);
if(this.fireEvent('beforcollapse', this, record, body, row.rowIndex) !== false){
this.state[record.id] = false;
Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
this.fireEvent('collapse', this, record, body, row.rowIndex);
}
}
});

Make sure you call your .js file from your index page, something that looks like:


<script type="text/javascript" src="libs/js/ext/ux/GridRowExpander.js"></script>


Now, in your GridPanel config options, add the following lines:

Under your columns config add:

expander,

It might looks like:


columns: [
expander,
{header: 'Company', width: 160, sortable: true, dataIndex: 'company'},
{header: 'Price', width: 75, sortable: true, dataIndex: 'price'},
{header: 'Change', width: 75, sortable: true, renderer: change, dataIndex: 'change'},
{header: '% Change', width: 75, sortable: true, renderer: pctChange, dataIndex: 'pctChange'},
{header: 'Last Updated', width: 100, sortable: true,
renderer: Ext.util.Format.dateRenderer('m/d/Y'),
dataIndex: 'lastChange'}
],
And then add this to the GridPanel config options:


plugins: expander,


Create the expander variable like this (it can be anywhere in your code, as long as the GridPanel has scope:

// Plugin for gridRowExpander which calls data from an external source
var expander = new Ext.grid.RowExpander({
remoteDataMethod : expandGrid
});

Finally, add your new function, expandGrid anywhere in your code:


// Remotely populates grid using data
function expandGrid(record,index){
Ext.get('remData'+index).load({
url: "your_external_file.html",
scripts: true,
params: ""
});
};


And that should do it! Thanks to the OP for the original plugin code. Cheers!

_Sergio_
6 May 2008, 3:46 AM
(dont forget to reset the state's with the for loop I posted more top)

Regards,

Iveco

This behaviour should be implemented in the RowExpander itself.
At the end of init method in RowExpander add


init : function(grid){
//original code....
grid.getStore().on("load", function(store, records, options){
Ext.select('div.x-grid3-row-expanded').replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
this.state, this.bodyContent = {};
}, this);

s.kerroumi
6 May 2008, 4:02 AM
Hello,

im working with the RowExpander and i m trying to find how to nest two grid inside a master grid like this :

master grid
------------>slave grid
----------------> slave "slave" grid

for the moment i have the following result (grid without headers except the last one)

6625


but the deepest grid has a strange behavior : it does'nt collapse correctly

here is my code :



var dayGrid = function(gridID, title, data){

var xg = Ext.grid;

// shared reader
var reader = new Ext.data.ArrayReader({}, [
{name: 'id'},
{name: 'day'},
{name: 'quantitytheo', type: 'float'},
{name: 'quantitysettled', type: 'float'}
]);

////////////////////////////////////////////////////////////////////////////////////////
// DAY GRID
////////////////////////////////////////////////////////////////////////////////////////

// row expander
// Issue template

// row expander
var expander = new Ext.grid.RowExpander({
remoteDataMethod : borrowlendGrid
});

var masterGrid = new xg.GridPanel({
ds: new Ext.data.Store({
reader: reader,
data: xg.dummyData
}),
cm: new xg.ColumnModel([
expander,
{header: "Day", width: 200, sortable: true, dataIndex: 'day'},
{header: "Theoretical Quantity", width: 200, sortable: true, dataIndex: 'quantitytheo'},
{header: "Settled Quantity", width: 200, sortable: true, dataIndex: 'quantitysettled'}
]),
viewConfig: {
forceFit:true
},
autoHeight: true,
hideHeaders: true,
width: 800,
height: 800,
plugins: expander,
collapsible: true,
animCollapse: false,
disableSelection : true,
trackMouseOver : false,
enableHdMenu : false,
renderTo: document.getElementById('testGrid')

});

}

////////////////////////////////////////////////////////////////////////////////////////
// BORROW LEND GRID
////////////////////////////////////////////////////////////////////////////////////////
var borrowlendGrid = function(record, index) {

var xg = Ext.grid;

var blreader = new Ext.data.ArrayReader({}, [
{name: 'id'},
{name: 'type'},
{name: 'theoQty'},
{name: 'theoQtyValue', type: 'float'},
{name: 'settledQty'},
{name: 'settledQtyValue', type: 'float'}
]);

var blstore = new Ext.data.Store({
reader: blreader,
data: xg.dummyData2
});

var iexpander = new Ext.grid.RowExpander({
remoteDataMethod : detailedMovementGrid,
tpl: new Ext.Template(
'<div id="subRemData"></div>')
});

var blgrid = new xg.GridPanel({
ds: blstore,
cm: new xg.ColumnModel([
iexpander,
{header: "Type", width: 200, sortable: true, dataIndex: 'type'},
{header: "Theoretical", width: 80, sortable: true, dataIndex: 'theoQty'},
{header: "Theoretical Quantity", width: 80, sortable: true, dataIndex: 'theoQtyValue'},
{header: "Settled", width: 80, sortable: true, dataIndex: 'settledQty'},
{header: "Settled Quantity", width: 80, sortable: true, dataIndex: 'settledQtyValue'}
]),

viewConfig: {
forceFit:true
},
disableSelection : false,
hideHeaders: true,
autoHeight: true,
plugins: iexpander,
collapsible: false,
disableSelection : true,
enableHdMenu : false,
trackMouseOver : false,
autoSizeColumns: true
});

blgrid.render(Ext.get('remData'+record.data.id));

};


////////////////////////////////////////////////////////////////////////////////////////
// DETAILLED MOVEMENTS GRID
////////////////////////////////////////////////////////////////////////////////////////

var detailedMovementGrid = function(record, index) {
var id = record.get("code");
var xg = Ext.grid;
var detailedreader = new Ext.data.ArrayReader({}, [
{name: 'id'},
{name:'code'},
{name: 'name'},
{name: 'tradeRef'},
{name: 'S'},
{name: 'T'},
{name: 'qty'},
{name: 'start'},
{name: 'end'},
{name: 'loanCcy'},
{name: 'loanValue'},
{name: 'rate'},
{name: 'divReq'}

]);

var detailedstore = new Ext.data.Store({
reader: detailedreader,
data: xg.dummyData3
});



var detailedgrid = new xg.GridPanel({
ds: detailedstore,
cm: new xg.ColumnModel([
{header: 'Client Code', width: 50, sortable: true, dataIndex: 'code'},
{header: 'Client Name', width: 120, sortable: true, dataIndex: 'name'},
{header: 'Trade Ref', width: 50, sortable: true, dataIndex: 'tradeRef'},
{header: 'S', width: 20, sortable: true, dataIndex: 'S'},
{header: 'T', width: 20, sortable: true, dataIndex: 'T'},
{header: 'Quantity', width: 60, sortable: true, dataIndex: 'qty'},
{header: 'Start Date', width: 60, sortable: true, dataIndex: 'start'},
{header: 'End Date', width: 60, sortable: true, dataIndex: 'end'},
{header: 'Loan Ccy', width: 60, sortable: true, dataIndex: 'loanCcy'},
{header: 'Loan Value', width: 60, sortable: true, dataIndex: 'loanValue'},
{header: 'rate', width: 60, sortable: true, dataIndex: 'rate'},
{header: 'Div Req', width: 60, sortable: true, dataIndex: 'divReq'},
]),

viewConfig: {
forceFit:true
},

autoHeight: true,
collapsible: false,
disableSelection : true,
trackMouseOver : false,
autoSizeColumns: true
});

detailedgrid.render(Ext.get('remData'+record.data.id));

};


/*** functions ***/


function issueLoad() {
Ext.QuickTips.init();
Ext.BLANK_IMAGE_URL = '/static/images/transparentpixel.gif';
new dayGrid("mastergrid", "Host Issues", Ext.grid.dummydata);
}

Ext.onReady(issueLoad);

s.kerroumi
6 May 2008, 5:29 AM
Hello,

im working with the RowExpander and i m trying to find how to nest two grid inside a master grid like this :

master grid
------------>slave grid
----------------> slave "slave" grid

for the moment i have the following result (grid without headers except the last one)

6625


but the deepest grid has a strange behavior : it does'nt collapse correctly

here is my code :



var dayGrid = function(gridID, title, data){

var xg = Ext.grid;

// shared reader
var reader = new Ext.data.ArrayReader({}, [
{name: 'id'},
{name: 'day'},
{name: 'quantitytheo', type: 'float'},
{name: 'quantitysettled', type: 'float'}
]);

////////////////////////////////////////////////////////////////////////////////////////
// DAY GRID
////////////////////////////////////////////////////////////////////////////////////////

// row expander
// Issue template

// row expander
var expander = new Ext.grid.RowExpander({
remoteDataMethod : borrowlendGrid
});

var masterGrid = new xg.GridPanel({
ds: new Ext.data.Store({
reader: reader,
data: xg.dummyData
}),
cm: new xg.ColumnModel([
expander,
{header: "Day", width: 200, sortable: true, dataIndex: 'day'},
{header: "Theoretical Quantity", width: 200, sortable: true, dataIndex: 'quantitytheo'},
{header: "Settled Quantity", width: 200, sortable: true, dataIndex: 'quantitysettled'}
]),
viewConfig: {
forceFit:true
},
autoHeight: true,
hideHeaders: true,
width: 800,
height: 800,
plugins: expander,
collapsible: true,
animCollapse: false,
disableSelection : true,
trackMouseOver : false,
enableHdMenu : false,
renderTo: document.getElementById('testGrid')

});

}

////////////////////////////////////////////////////////////////////////////////////////
// BORROW LEND GRID
////////////////////////////////////////////////////////////////////////////////////////
var borrowlendGrid = function(record, index) {

var xg = Ext.grid;

var blreader = new Ext.data.ArrayReader({}, [
{name: 'id'},
{name: 'type'},
{name: 'theoQty'},
{name: 'theoQtyValue', type: 'float'},
{name: 'settledQty'},
{name: 'settledQtyValue', type: 'float'}
]);

var blstore = new Ext.data.Store({
reader: blreader,
data: xg.dummyData2
});

var iexpander = new Ext.grid.RowExpander({
remoteDataMethod : detailedMovementGrid,
tpl: new Ext.Template(
'<div id="subRemData"></div>')
});

var blgrid = new xg.GridPanel({
ds: blstore,
cm: new xg.ColumnModel([
iexpander,
{header: "Type", width: 200, sortable: true, dataIndex: 'type'},
{header: "Theoretical", width: 80, sortable: true, dataIndex: 'theoQty'},
{header: "Theoretical Quantity", width: 80, sortable: true, dataIndex: 'theoQtyValue'},
{header: "Settled", width: 80, sortable: true, dataIndex: 'settledQty'},
{header: "Settled Quantity", width: 80, sortable: true, dataIndex: 'settledQtyValue'}
]),

viewConfig: {
forceFit:true
},
disableSelection : false,
hideHeaders: true,
autoHeight: true,
plugins: iexpander,
collapsible: false,
disableSelection : true,
enableHdMenu : false,
trackMouseOver : false,
autoSizeColumns: true
});

blgrid.render(Ext.get('remData'+record.data.id));

};


////////////////////////////////////////////////////////////////////////////////////////
// DETAILLED MOVEMENTS GRID
////////////////////////////////////////////////////////////////////////////////////////

var detailedMovementGrid = function(record, index) {
var id = record.get("code");
var xg = Ext.grid;
var detailedreader = new Ext.data.ArrayReader({}, [
{name: 'id'},
{name:'code'},
{name: 'name'},
{name: 'tradeRef'},
{name: 'S'},
{name: 'T'},
{name: 'qty'},
{name: 'start'},
{name: 'end'},
{name: 'loanCcy'},
{name: 'loanValue'},
{name: 'rate'},
{name: 'divReq'}

]);

var detailedstore = new Ext.data.Store({
reader: detailedreader,
data: xg.dummyData3
});



var detailedgrid = new xg.GridPanel({
ds: detailedstore,
cm: new xg.ColumnModel([
{header: 'Client Code', width: 50, sortable: true, dataIndex: 'code'},
{header: 'Client Name', width: 120, sortable: true, dataIndex: 'name'},
{header: 'Trade Ref', width: 50, sortable: true, dataIndex: 'tradeRef'},
{header: 'S', width: 20, sortable: true, dataIndex: 'S'},
{header: 'T', width: 20, sortable: true, dataIndex: 'T'},
{header: 'Quantity', width: 60, sortable: true, dataIndex: 'qty'},
{header: 'Start Date', width: 60, sortable: true, dataIndex: 'start'},
{header: 'End Date', width: 60, sortable: true, dataIndex: 'end'},
{header: 'Loan Ccy', width: 60, sortable: true, dataIndex: 'loanCcy'},
{header: 'Loan Value', width: 60, sortable: true, dataIndex: 'loanValue'},
{header: 'rate', width: 60, sortable: true, dataIndex: 'rate'},
{header: 'Div Req', width: 60, sortable: true, dataIndex: 'divReq'},
]),

viewConfig: {
forceFit:true
},

autoHeight: true,
collapsible: false,
disableSelection : true,
trackMouseOver : false,
autoSizeColumns: true
});

detailedgrid.render(Ext.get('remData'+record.data.id));

};


/*** functions ***/


function issueLoad() {
Ext.QuickTips.init();
Ext.BLANK_IMAGE_URL = '/static/images/transparentpixel.gif';
new dayGrid("mastergrid", "Host Issues", Ext.grid.dummydata);
}

Ext.onReady(issueLoad);



no responses?


in fact in my nested grid the passage from "x-grid3-row x-grid3-row-expanded" to "x-grid3-row x-grid3-row-collapsed" has no effect...

can someone please tell me if somebody met that issue...

amanbrar82
1 Jun 2008, 2:08 AM
2) When I have a row expanded and showing the rowExpander, meanwhile updating the grid's datastore (though refreshing the grid), it will show all rows collapsed, even if some of them before the update expanded - Iveco

I am using rowExpander plugin as given in ExtJs examples folder. It is working nicely, but on store reload, all rows become collapsed though some were in expanded state before refresh. how can I keep rows expanded. I saw the code given in above quoted post, but could not get it to work, Please help me!!! Here is simplified version of my code..

var expander = new Ext.grid.RowExpander({
tpl : new Ext.Template('This is a test grid<br>Testing')
});

var mystore = new Ext.data.Store({
url: 'plantsdetail.xml',
reader: new Ext.data.XmlReader({
record: 'plant'
}, [ 'id2','nature','useful' ])
});

mystore.load();

var cm = new Ext.grid.ColumnModel([
expander,
{header: "Id", width: 80, sortable: true, dataIndex: 'id2', id:'Id'},
{header: "Nature", width: 140, sortable: true, dataIndex: 'nature'},
{header: "Useful", width: 140, sortable: true, dataIndex: 'useful'}
]);

var mygrid = new Ext.grid.GridPanel({
store: mystore,
cm: cm,
width: 400,
height: 300,
plugins: expander,
title: 'Expander Rows',
renderTo: document.body
});

function store_reload(){
mystore.reload()
}

ParanoidAndroid
1 Jul 2008, 7:14 AM
Got this fixed by using "rowdblclick".



Can you (or someone) show me an example of how to attach this? I'm trying:


// ...
listeners : {
'rowdblclick': rowExpander.toggleRow,
'mouseover':chooseActions
}
... //

What I want to happen is to have the doubleclick call the rowExpander's toggleRow method. So far nothing I've tried seems to work. Thanks for any help.

ParanoidAndroid
2 Jul 2008, 5:11 AM
Turns out I found the solution. I post it here for others who may have this problem.

listeners : {
'rowdblclick': function() { rowExpander.toggleRow(gridSelectionModel.grid.view.getRow(gridSelectionModel.last) )
},
'mouseover':chooseActions
}

... where gridSelectionModel is an Ext.grid.RowSelectionModel.

I really couldn't find anything about this in the docs. The "last" property isn't even mentioned in the properties (actually, there are no properties listed). Maybe my poor searching, but I had to analyze the objects and their properties to even come up with this solution.

Call me crazy, but I think the RowSelectionModel should have a method that returns the row or row index.

ethraza
14 Jul 2008, 11:46 PM
This post has helped me a lot, so I coming back to give my contribution.

Added:
- Loading class
- store parameter
- render event

So now this rowExpander can load and apply another store into the tpl.

I have added to this rowExpander the possibility to pass a Data Store to it. The store will be loaded with the value for the id of the row record as param and the result will be applied to the tpl set. After all it will fire the render event passing this.store and record at the end.

RowExpander:


Ext.grid.RowExpander = function(config){
Ext.apply(this, config);
Ext.grid.RowExpander.superclass.constructor.call(this);

if(this.tpl){
if(typeof this.tpl == 'string'){
this.tpl = new Ext.Template(this.tpl);
}
this.tpl.compile();
}

this.state = {};
this.bodyContent = {};

this.addEvents({
beforeexpand : true,
expand: true,
beforecollapse: true,
collapse: true,
render: true
});
};

Ext.extend(Ext.grid.RowExpander, Ext.util.Observable, {
header: "",
width: 20,
sortable: false,
fixed:true,
dataIndex: '',
id: 'expander',
lazyRender : true,
enableCaching: true,

getRowClass : function(record, rowIndex, p, ds){
p.cols = p.cols-1;
var content = this.bodyContent[record.id];
if(!content && !this.lazyRender){
content = this.getBodyContent(record, rowIndex);
}
if(content){
p.body = content;
}
return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';
},

init : function(grid){
this.grid = grid;

var view = grid.getView();
view.getRowClass = this.getRowClass.createDelegate(this);

view.enableRowBody = true;

grid.on('render', function(){
view.mainBody.on('mousedown', this.onMouseDown, this);
}, this);
},

getBodyContent : function(record, index){
if(!this.enableCaching){
return this.tpl.apply(record.data);
}
var content = this.bodyContent[record.id];
if(!content){
content = this.tpl.apply(record.data);
this.bodyContent[record.id] = content;
}
return content;
},
// Setter and Getter methods for the remoteDataMethod property
setRemoteDataMethod : function (fn){
this.remoteDataMethod = fn;
},

getRemoteDataMethod : function (record, index){
if(!this.remoteDataMethod){
return;
}
return this.remoteDataMethod.call(this,record,index);
},

onMouseDown : function(e, t){
if(t.className == 'x-grid3-row-expander'){
e.stopEvent();
var row = e.getTarget('.x-grid3-row');
this.toggleRow(row);
}
},

renderer : function(v, p, record){
p.cellAttr = 'rowspan="2"';
return '<div class="x-grid3-row-expander"> </div>';
},

beforeExpand : function(record, body, rowIndex){
if(this.fireEvent('beforexpand', this, record, body, rowIndex) !== false){
// If remoteDataMethod is defined then we'll need a div, with a unique ID,
// to place the content
// If store is defined it will load up passing id:record.id (grid store)
// in params, use the tpl defined to apply response record (rec)
// and fire the render event with won store and record
if(this.remoteDataMethod){
this.tpl = new Ext.Template("<div id='remData" + rowIndex + "' class='rem-data-expand'><\div>");
}else if(this.store && this.tpl && this.lazyRender){
if(body.innerHTML == '' || !this.enableCaching){
body.innerHTML = Ext.UpdateManager.defaults.indicatorText;
this.store.on('load', function(s,rec){
body.innerHTML = '';
Ext.each(rec,function(e){
body.innerHTML += this.getBodyContent(e, rowIndex);
},this);

this.fireEvent('render', this, record, body, rowIndex, s, rec);
},this,{
scope: this,
single: true
});
this.store.load({params: {id: record.id}});
}
return true;
}

if(this.tpl && this.lazyRender){
body.innerHTML = this.getBodyContent(record, rowIndex);
this.fireEvent('render', this, record, body, rowIndex);
}

return true;
}else{
return false;
}
},

toggleRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);
},

expandRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
var record = this.grid.store.getAt(row.rowIndex);
var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
if(this.beforeExpand(record, body, row.rowIndex)){
this.state[record.id] = true;
Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');
if(this.fireEvent('expand', this, record, body, row.rowIndex) !== false){
// If the expand event is successful then get the remoteDataMethod
this.getRemoteDataMethod(record,row.rowIndex);
}
}
},

collapseRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
var record = this.grid.store.getAt(row.rowIndex);
var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);
if(this.fireEvent('beforcollapse', this, record, body, row.rowIndex) !== false){
this.state[record.id] = false;
Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
this.fireEvent('collapse', this, record, body, row.rowIndex);
}
}
});
An use example where MemberDS = Ext.data.Store(...):

var expander = new Ext.grid.RowExpander({
store: MemberDS,
tpl : new Ext.XTemplate(
'<fieldset><legend>Profile</legend>',
'<tpl if="pic_id">',
'<img id="img_{pic_id}" src="/pics/{pic_id}?'+new Date().getTime()+'" /><br>'
'</tpl>',
'<b>ID:</b> {codea} {codeb}<br>',
'<b>Gender:</b> {gender}<br>',
'<b>DOB:</b> {dob}<br>',
'<b>Category:</b> {category}<br>',
'<b>Comments:</b> {comments}',
'</fieldset>'
)
});

ricardolowe
16 Jul 2008, 5:35 AM
I am working an an EditorGridPanel in version 2.1. I searching for clues on how to wrap some Ext components into a RowExpander object.

Am I on the right path? I can see how to use the XTemplate to dump data into a RowExpander, but how would I go about dumping the Ext components (combobox,datefield,etc) for the related parent grid row into the RowExpander?

Any thoughts?

Only solution that comes to mind is to pass the datastore for a combobox and do <tpl for loop> over the data to build an HTML dropdown ( <select id={parentrow.id} > ), etc. That feels cheesy for some reason. Would rather use the Ext components.

ricardolowe
16 Jul 2008, 7:34 AM
I update your code to use the store reader's id value instead of the literal 'id'

instead of

beforeExpand: function(record, body, rowIndex){
blah....
}else if(this.store && this.tpl && this.lazyRender){
blah... blah...
this.store.load({params: {id: record.id}});

..i do this...

beforeExpand: function(record, body, rowIndex){
blah....
}else if(this.store && this.tpl && this.lazyRender){
blah... blah...
var params = {};
params[this.store.reader.meta.id] = record.id;
this.store.load({params: params});
Perhaps there is a cleaner way to get the store's reader and its properties?

wolf
19 Jul 2008, 5:36 PM
Only solution that comes to mind is to pass the datastore for a combobox and do <tpl for loop> over the data to build an HTML dropdown ( <select id={parentrow.id} > ), etc. That feels cheesy for some reason. Would rather use the Ext components.

I needed something similar. My solution is exactly that. In the tpl, I gave <input type=text id="customFIeld-row{rowIndex}-field1" >.



tpl : new Ext.XTemplate(
// TODO maybe use form.Field.applyTo on these form input items on expand?
'Account Code: <input type="text" id="customField-row{rowIndex}-field1" name="accountCode" value="{accountCode}" /><br>',
'Product Code: <input type="text" id="customField-row{rowIndex}-field2" name="projectCode" value="{projectCode}" />'
)


To get rowIndex I had to change RowExpander.getBodyContent().


// Also apply rowIndex in addition to record.data.
content = this.tpl.apply( Ext.apply( {rowIndex: index}, record.data ) )


I used the RowExpander with a store provided earlier in this thread.



expander.on("render", function( pExpander, pRecord, pBody, pRowIndex, pStore, pRecords) {
if ( ! this.expandingPanel ) {
this.expandingPanel = [];
}

this.expandingPanel[pRowIndex] = new Ext.Panel(
{
// title: 'Custom Fields',
layout:'fit',
// monitorResize: true,
// autoDestroy: false,
// height: 300,
items: [
{
xtype: 'textfield',
applyTo: 'customField-row' + pRowIndex + '-field1',
fieldLabel: 'Account Code',

...


I haven't quite worked out how to make the panel look good, that is, use the field labels, but it works for now. Still in progress...

I had gone the Panel.renderTo approach and used a div in the tpl, but the content disappeared after the 2nd expand on each row and the data would be lost.

sshahdb
7 Aug 2008, 8:53 AM
Good Job, thanks for plugin - helped me alot

hecules
10 Aug 2008, 6:48 PM
Hello,everybuddy:
I use the plugin in an pre-configured grid class. I found a bug: when I clicked the expander plugin, the icon changed but not content diplay. I inserted an alert into the remotemethod, I found that the remotemethod had been called. What happened? I check out the source code and maybe found the source of it. I fixed this bug with following code,perhaps it will help somebody.
By the way, I use this plugin by:


initComponent: function(){
this.expander = new Ext.ux.RowExpander({
remoteDataMethod : this.getExpanderContent
});
Ext.apply(this, {
store: this.ds,

columns: [
this.expander,
............
]
,plugins:[this.cellActions,this.expander]
});
}


/*
* Ext JS Library 2.0
* Copyright(c) 2006-2007, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*
* MODIFIED: SGB [12.12.07]
* Added support for a new config option, remoteDataMethod,
* including getter and setter functions, and minor mods
* to the beforeExpand and expandRow functions
* lisihao: ****,this plugin has some bugs, so can't dispaly content when toggle the row.I fix it.RI!
*/

Ext.ux.RowExpander = function(config){
Ext.apply(this, config);
Ext.ux.RowExpander.superclass.constructor.call(this);

if(this.tpl){
if(typeof this.tpl == 'string'){
this.tpl = new Ext.Template(this.tpl);
}
this.tpl.compile();
}

this.state = {};
this.bodyContent = {};

this.addEvents({
beforeexpand : true,
expand: true,
beforecollapse: true,
collapse: true
});
};

Ext.extend(Ext.ux.RowExpander, Ext.util.Observable, {
header: "",
width: 20,
sortable: false,
fixed:true,
dataIndex: '',
id: 'expander',
lazyRender : true,
enableCaching: true,

getRowClass : function(record, rowIndex, p, ds){
p.cols = p.cols-1;
var content = this.bodyContent[record.id];
if(!content && !this.lazyRender){
content = this.getBodyContent(record, rowIndex);
}
if(content){
p.body = content;
}
return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';
},

init : function(grid){
this.grid = grid;

var view = grid.getView();
view.getRowClass = this.getRowClass.createDelegate(this);

view.enableRowBody = true;

grid.on('render', function(){
view.mainBody.on('mousedown', this.onMouseDown, this);
}, this);
},

getBodyContent : function(record, index){
if(!this.enableCaching){
return this.tpl.apply(record.data);
}
var content = this.bodyContent[record.id];
if(!content){
content = this.tpl.apply(record.data);
this.bodyContent[record.id] = content;
}
return content;
},
// Setter and Getter methods for the remoteDataMethod property
setRemoteDataMethod : function (fn){
this.remoteDataMethod = fn;
},

getRemoteDataMethod : function (record, index){
if(!this.remoteDataMethod){
return;
}
return this.remoteDataMethod.call(this,record,index);
},

onMouseDown : function(e, t){
if(t.className == 'x-grid3-row-expander'){
e.stopEvent();
var row = e.getTarget('.x-grid3-row');
this.toggleRow(row);
}
},

renderer : function(v, p, record){
p.cellAttr = 'rowspan="2"';
return '<div class="x-grid3-row-expander"> </div>';
},

beforeExpand : function(record, body, rowIndex){

if(this.fireEvent('beforexpand', this, record, body, rowIndex) !== false){
// If remoteDataMethod is defined then we'll need a div, with a unique ID,
// to place the content
if(this.remoteDataMethod){
this.tpl = new Ext.Template("<div id='remData" + rowIndex + "' class='rem-data-expand'><\div>");
}
if(this.tpl && this.lazyRender){
// body.innerHTML = this.getBodyContent(record, rowIndex);
body.innerHTML = '<div id="remData"' + rowIndex + '" class="rem-data-expand">'+this.getRemoteDataMethod(record, rowIndex)+'</div>';// >>>>>--- change here!!!
}

return true;
}else{
return false;
}
},

toggleRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);
},

expandRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
var record = this.grid.store.getAt(row.rowIndex);
var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
if(this.beforeExpand(record, body, row.rowIndex)){
this.state[record.id] = true;
Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');
if(this.fireEvent('expand', this, record, body, row.rowIndex) !== false){
// If the expand event is successful then get the remoteDataMethod
this.getRemoteDataMethod(record,row.rowIndex);
}
}
},

collapseRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
var record = this.grid.store.getAt(row.rowIndex);
var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);
if(this.fireEvent('beforcollapse', this, record, body, row.rowIndex) !== false){
this.state[record.id] = false;
Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
this.fireEvent('collapse', this, record, body, row.rowIndex);
}
}
});

hecules
11 Aug 2008, 5:29 AM
thanks, i post the reply in incorrect style. You fix it!

wolf
13 Aug 2008, 12:35 PM
ricardolowe was asking how to put Ext fields into the expanded row. I had the same requirement. I initially thought I had it solved but found problems. See here now the working solution which allows combo boxes, text boxes, etc in the expanded row content without using a Template. Just override a method to return Ext Panel items. The overridden method has access to the grid.store record of the row OR a record of a store passed in the config parm of RowExpander.

Here's the abstract class:



/**
* RowExpander changed from RowExpander.js in the Ext examples and some ideas taken
* from the forum (http://extjs.com/forum/showthread.php?t=21017&page=3).
*
* Override the createExpandingRowPanelItems function to make Ext expanded row content
* (as opposed to using Ext.Template to make the expanded row content).
*
* If config.store is passed in, pass a record for the row from that store instead
* of the grid store into the createExpandingRowPanelItems function.
*/
Ext.grid.RowExpander = function(config){
Ext.apply(this, config);
Ext.grid.RowExpander.superclass.constructor.call(this);

this.state = {};

this.addEvents({
beforeexpand : true,
expand: true,
beforecollapse: true,
collapse: true
});
};

Ext.extend(Ext.grid.RowExpander, Ext.util.Observable, {
header: "",
width: 20,
sortable: false,
fixed:true,
dataIndex: '',
id: 'expander',
lazyRender : true,
enableCaching: true,

getRowClass : function(record, rowIndex, rowParams, ds){
// cols: The column count to apply to the body row's TD colspan attribute (defaults to the current column count of the grid).
rowParams.cols = rowParams.cols-1; // make it the width of the whole row
return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';
},

init : function(grid){
this.grid = grid;

var view = grid.getView();
view.getRowClass = this.getRowClass.createDelegate(this);

view.enableRowBody = true;

grid.on('render', function(){
view.mainBody.on( 'mousedown', this.onMouseDown, this );
}, this);

// store
grid.getStore().on("load", function(store, records, options){
Ext.select('div.x-grid3-row-expanded').replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
this.state = {};
}, this);

this.store.load(); // load here instead of in beforeExpand cuz that would wipe out additions to store
},

onMouseDown : function( e, t ) {
if(t.className == 'x-grid3-row-expander'){
e.stopEvent();
var row = e.getTarget('.x-grid3-row');
this.toggleRow(row);
}
},

renderer : function(v, p, record){
p.cellAttr = 'rowspan="2"';
return '<div class="x-grid3-row-expander"> </div>';
},

beforeExpand : function(record, rowBody, rowIndex){
var isContinue = true;
if(this.fireEvent('beforeexpand', this, record, rowBody, rowIndex, this.store) !== false){
if(rowBody.innerHTML == '' || !this.enableCaching) {
this.createExpandingRowPanel( record, rowBody, rowIndex );
}
} else {
isContinue = false;
}

return isContinue;
},

toggleRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);
},

expandRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
var record = this.grid.store.getAt(row.rowIndex);
// if using additional store passed in config, pass record from it instead of from the grid store
var recordToPass = this.store ? this.store.getAt(row.rowIndex) : record;
var rowBody = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
if(this.beforeExpand(recordToPass, rowBody, row.rowIndex)){
this.state[record.id] = true;
Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');
this.fireEvent('expand', this, recordToPass, rowBody, row.rowIndex);
}
},

collapseRow : function(row){
if(typeof row == 'number'){
row = this.grid.view.getRow(row);
}
var record = this.grid.store.getAt(row.rowIndex);
// if using additional store passed in config, pass record from it instead of from the grid store
var recordToPass = this.store ? this.store.getAt(row.rowIndex) : record;
var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);
if(this.fireEvent('beforcollapse', this, recordToPass, body, row.rowIndex) !== false){
this.state[record.id] = false;
Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
this.fireEvent('collapse', this, recordToPass, body, row.rowIndex);
}
},

createExpandingRowPanel: function( record, rowBody, rowIndex ) {

// record.id is more stable than rowIndex for panel item's key; rows can be deleted.
var panelItemIndex = record.id;
// var panelItemIndex = rowIndex;

// init array of expanding row panels if not already inited
if ( !this.expandingRowPanel ) {
this.expandingRowPanel = [];
}

// Add a new panel to the row body if not already there
if ( !this.expandingRowPanel[panelItemIndex] ) {
this.expandingRowPanel[panelItemIndex] = new Ext.Panel(
{
// title: 'Custom Fields',
// layout:'fit', // this doesn't put the labels there
border: false,
bodyBorder: false,
layout:'form',
renderTo: rowBody,
items: this.createExpandingRowPanelItems( record, rowIndex )
}
);
}
},

/**
* Override this method to put Ext form items into the expanding row panel.
* @return Array of panel items.
*/
createExpandingRowPanelItems: function( record, rowIndex ) {
var panelItems = [];

return panelItems;
}

});

wolf
15 Aug 2008, 2:17 PM
FYI, in the code in that last post, the ext fields created by the createExpandingRowPanelItems() function are not created unless the row is expanded. But the store can contain the data from those ext fields if you do something like this in your ext field:



listeners: {
'change' : this.onChangeField,
scope: this
},
record: record,
fieldKey: 'myFieldKey',





onChangeField: function( field, newValue, oldValue ) {
// copy the new field value into the store
field.record.set( field.fieldKey, newValue );
}


Also, if you do row deletion, be careful of using "rowIndex"; the rowIndex for a row would change ;) I made a change to createExpandingRowPanel() in the above post for this reason.

nazextjs
23 Aug 2008, 8:09 PM
I am using a grid to display the data along with a RowExpander.
When the expand the row it does show the fields but without values which I really want to get to. The other issue is I want to learn how to override the createExpandingRowPanelItems function in the RowExpander class. I dont think I am doing it right.



// row expander
var expander = new Ext.grid.RowExpander({
store: store,
createExpandingRowPanelItems:
function( record, rowIndex ) {
var panelItems = [{
id: 'form-row'+rowIndex,
columnWidth: 0.4,
xtype: 'fieldset',
labelWidth: 90,
//title:'Company details',
defaults: {width: 140}, // Default config options for child items
defaultType: 'textfield',
autoHeight: true,
bodyStyle: Ext.isIE ? 'padding:0 0 5px 15px;' : 'padding:10px 15px;',
border: false,
style: {
"margin-left": "10px", // when you add custom margin in IE 6...
"margin-right": Ext.isIE6 ? (Ext.isStrict ? "-10px" : "-13px") : "0" // you have to adjust for it somewhere else
},
items: [{
fieldLabel: 'Name',
name: 'barcode'
},{
fieldLabel: 'Price',
name: 'title'
},{
fieldLabel: '% Change',
name: 'author'
},{
xtype: 'datefield',
fieldLabel: 'Last Updated',
name: 'barcode'
}]
}];
return panelItems;
}
});

wolf
27 Aug 2008, 6:03 AM
http://extjs.com/learn/Manual:Intro:Inheritance

RayZ
16 Oct 2008, 11:10 PM
Hello, im working with the RowExpander and i m trying to find how to nest two grid inside a master grid like this :
master grid
------------>slave grid
----------------> slave "slave" grid

but the deepest grid has a strange behavior : it does'nt collapse correctly
here is my code :



....
var iexpander = new Ext.grid.RowExpander({
remoteDataMethod : detailedMovementGrid,
tpl: new Ext.Template(
'<div id="subRemData"></div>')
});
....

You're trying generate duplicate "slave" ID divs? It's might be wrong...?

henryivy
27 Jan 2009, 7:57 AM
Hi,

I refered the above sample and I implemented the sample nested grid using GridPanel 2.2. It is not expand the child grid. I got error from firebox during the childGrid = function(record, index) execution time.The error message is Ext={version:"2.2"};window["undefined"]=window["undefined"];Ext.apply=function(C,D,B{if({Ext.apply(C,B}if(C&&D&&typeof D=="object"){for(var A in D){C[A]=D[A]}}return C};(function(){var idSeed=0;var ua=navigator.userAgent.toLowerCase();

I have included the my sample code.


Ext.onReady(function() {
var xg = Ext.grid;
// shared reader
var reader = new Ext.data.ArrayReader({}, [
{ name: 'id' },
{ name: 'type' },
]);
var dataType = [
[10, 'Published'],
[11, 'UnPublished']]

// row expander
var expander = new Ext.grid.RowExpander({
remoteDataMethod:childGrid ,
tpl: new Ext.Template('<div id="subRemData"></div>')
});
var masterGrid = new xg.GridPanel({
store: new Ext.data.Store({
reader: reader,
data: dataType
}),
cm: new xg.ColumnModel([
expander,
{ header: "Type", width: 200, sortable: true, dataIndex: 'type' }
]),
viewConfig: {
forceFit: true
},
autoHeight: true,
hideHeaders: false,
width: 800,
height: 800,
plugins: expander,
collapsible: true,
animCollapse: false,
disableSelection: true,
trackMouseOver: false,
enableHdMenu: false,
renderTo: document.getElementById('Div1')
});

// Child grid

var dataPGName = [
[10, 10, 'Retail Food'],
[11, 11, 'Chemical']]

var childGrid = function(record, index) {
var id = record.get("code");
var xg = Ext.grid;
var detailedreader = new Ext.data.ArrayReader({}, [
{ name: 'id' },
{ name: 'code' },
{ name: 'name' },
]);
var detailedstore = new Ext.data.Store({
reader: detailedreader,
data: dataPGName
});
var detailedgrid = new xg.GridPanel({
store: detailedstore,
cm: new xg.ColumnModel([
{ header: 'PG ID ', width: 50, sortable: true, dataIndex: 'code' },
{ header: 'PG Name', width: 120, sortable: true, dataIndex: 'name' }
]),
autoHeight: true,
collapsible: false,
disableSelection: true,
trackMouseOver: false,
autoSizeColumns: true
});
detailedgrid.render(Ext.get('remData' + record.data.id));
};
})

Please help me anyone how to rectify that issues.

PulpMysteryFan
5 Mar 2009, 2:27 PM
I'm using the extended RowExpander on a project. It's great. However, we tweaked it a little. I'm fairly clueless when it comes to Javascript (not to mention ExtJS), but the reader may still find this of interest.

The problem was that we have pages with multiple RowExpanders (because there are multiple grids). But the id of the div being populated via getRemoteDataMethod() is not unique because it only looks at the row index. That meant only one "row 1" could be expanded, etc.

There are two changes:

1. We use a new "suffix" property to make the index unique. I tried to use "id", but when the calling routine tried to set "id" the expander didn't render. It wasn't worth figuring out, so I just added another property. Using "id" would be better.

2. The remoteDataMethod signature is a little different because we changed it to pass the div element itself. That way the view doesn't need to know the secret name used for the div.

Here's a snippet showing my changes:

getRemoteDataMethod : function (record, index, handle){
if(!this.remoteDataMethod){
return;
}
return this.remoteDataMethod.call(this, record, index, Ext.get(this.getExpanderId(index)));
},
getExpanderId: function(index){
// This is a customization.
return "remData_ " + index + "_" + this.suffix;
},
beforeExpand : function(record, body, rowIndex){
if(this.fireEvent('beforexpand', this, record, body, rowIndex) !== false){
// If remoteDataMethod is defined then we'll need a div, with a unique ID,
// to place the content
if(this.remoteDataMethod){
//this.tpl = new Ext.Template("<div id='remData" + rowIndex + "' class='rem-data-expand'><\div>");
this.tpl = new Ext.Template("<div id='" + this.getExpanderId(rowIndex) + "' class='rem-data-expand'><\div>");
}
if(this.tpl && this.lazyRender){
body.innerHTML = this.getBodyContent(record, rowIndex);
}
return true;
}else{
return false;
}
},

PV-Patrick
18 Jun 2009, 1:28 AM
This post has helped me a lot, so I coming back to give my contribution......

I am trying to get this working via your method ethraza, however I cannot get anything to show up in the expander TPL. I modified yours slightly to include additional parameters to pass to the store load, however that should not effect anything that I am aware of. The store loads fine and I get the correct JSON response. I even have a simple TPL that just has "Hi" in it. Any suggestions on what might be the problem? It just gets stuck with the Loading... icon inside the row.

jjulian
26 Jul 2009, 2:36 PM
I also had a requirement on a project to show panels within a RowExpander. I used wolf's version of the class from this thread (THANKS!), but I found that the expanded panel's layout was not correct. I tried BorderLayout, ColumnLayout, but they all were overlapping items. I solved it by calling renderTo OUTSIDE the config with a bit of a delay on it (100ms).

I've added a few other minor tweaks to it, such as passing on resize events from the grid, and destroying all saved panels on store load.

You can find the code in my ext-extensions project on github. I named it PanelRowExpander.

http://github.com/jjulian/ext-extensions/tree/master

cmadison0005
13 Mar 2012, 5:27 AM
Hi all, i had a question regarding the plugin above in the context of the ExtJS MVC acrhitecture. I want the rowExpander to inject a grid panel, as opposed to just changing the styling and using content from the current row. I found an extension (AjaxRowExpander) http://blog.tremend.ro/2008/04/22/extjs-gridpanel-plugin-ajax-row-expander-2/ which i am unable to integrate.

The examples i have found tend to create then entirety of the app within a single .js file, as opposed to the MVC type setup. My question is, how can i use the row expander to insert a container or panel of some sort such that i can place within it any component i desire?

I cannot seem to port over the plugins which are written in single pieces of script as i continually get errors telling me that Ext.grid is undefined. This is odd bc the first line of app.js is:

Ext.require(['ux.grid.RowExpander', 'ux.grid.AjaxRowExpander']);

subsequently, the require in rowExpander.js is:

requires: ['Ext.grid.feature.RowBody','Ext.grid.feature.RowWrap']

SO i think fundamentally im trying to recreate the plugin using Ext.define as opposed to containing it within a single app.js file.

Thanks for any advice!

eyp
4 Apr 2012, 1:59 AM
Hi, I'm trying to migrate from ExtJs 2.3 to 3.4 (I've thought it'll be easier than trying to migrate directly to 4.0.7).

In my app I was using this plugin, is it available for ExtJs 3?, is there another alternative?