PDA

View Full Version : RowPanelExpander for grids



steffenk
25 Feb 2010, 3:28 PM
This allows to use normal panels in RowExpander. I found the idea somewhere in the net and integrated it in RowExpander.

Online-Demo (http://dev.sk-typo3.de/ext/rowpanelexpander.html)

Updated 23/12/2010: make it stateful, corrected other mentioned issues, added expandAll and collapsAll methods



Here is the plugin:


Ext.ns('Ext.ux.grid');

/**
* @class Ext.ux.grid.RowPanelExpander
* @extends Ext.util.Observable
* Plugin (ptype = 'rowexpander') that adds the ability to have a Column in a grid which enables
* a second row body which expands/contracts. The expand/contract behavior is configurable to react
* on clicking of the column, double click of the row, and/or hitting enter while a row is selected.
*
* @ptype rowexpander
*/
Ext.ux.grid.RowPanelExpander = Ext.extend(Ext.util.Observable, {
/**
* @cfg {Boolean} expandOnEnter
* <tt>true</tt> to toggle selected row(s) between expanded/collapsed when the enter
* key is pressed (defaults to <tt>true</tt>).
*/
expandOnEnter : true,
/**
* @cfg {Boolean} expandOnDblClick
* <tt>true</tt> to toggle a row between expanded/collapsed when double clicked
* (defaults to <tt>true</tt>).
*/
expandOnDblClick : true,

header : '',
width : 20,
sortable : false,
fixed : true,
menuDisabled : true,
dataIndex : '',
id : 'expander',
lazyRender : true,
enableCaching : true,

constructor : function(config) {
Ext.apply(this, config);

this.addEvents( {
/**
* @event beforeexpand
* Fires before the row expands. Have the listener return false to prevent the row from expanding.
* @param {Object} this RowExpander object.
* @param {Object} Ext.data.Record Record for the selected row.
* @param {Object} body body element for the secondary row.
* @param {Number} rowIndex The current row index.
*/
beforeexpand : true,
/**
* @event expand
* Fires after the row expands.
* @param {Object} this RowExpander object.
* @param {Object} Ext.data.Record Record for the selected row.
* @param {Object} body body element for the secondary row.
* @param {Number} rowIndex The current row index.
*/
expand : true,
/**
* @event beforecollapse
* Fires before the row collapses. Have the listener return false to prevent the row from collapsing.
* @param {Object} this RowExpander object.
* @param {Object} Ext.data.Record Record for the selected row.
* @param {Object} body body element for the secondary row.
* @param {Number} rowIndex The current row index.
*/
beforecollapse : true,
/**
* @event collapse
* Fires after the row collapses.
* @param {Object} this RowExpander object.
* @param {Object} Ext.data.Record Record for the selected row.
* @param {Object} body body element for the secondary row.
* @param {Number} rowIndex The current row index.
*/
collapse : true
});

Ext.ux.grid.RowPanelExpander.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 = {};
},

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', this.onRender, this);
grid.store.on('load', this.onStoreLoaded, this);
grid.on('destroy', this.onDestroy, this);
grid.on("beforestaterestore", this.applyState, this);
grid.on("beforestatesave", this.saveState, this);
},

// @private
onRender : function() {
var grid = this.grid;


var mainBody = grid.getView().mainBody;
mainBody.on('mousedown', this.onMouseDown, this, {
delegate : '.x-grid3-row-expander'
});

grid.getView().on('rowremoved', this.onRowRemoved, this);
grid.getView().on('rowupdated', this.onRowUpdated, this);

if (this.expandOnEnter) {
this.keyNav = new Ext.KeyNav(this.grid.getGridEl(),
{
'enter' : this.onEnter,
scope : this
});
}
if (this.expandOnDblClick) {
grid.on('rowdblclick', this.onRowDblClick, this);
}
},

onStoreLoaded: function(store, records,options) {
var index = -1;
for(var key in this.state){
if (this.state[key] === true) {
index = store.indexOfId(key);
if (index > -1) {
this.expandRow(index);
}
}
}
},

/** @private */
applyState: function(grid, state){
this.suspendStateStore = true;
if(state.expander) {
this.state = state.expander;
}
this.suspendStateStore = false;
},

/** @private */
saveState: function(grid, state){
return state.expander = this.state;
},

/** @private */
onDestroy : function() {
if (this.keyNav) {
this.keyNav.disable();
delete this.keyNav;
}
/*
* A majority of the time, the plugin will be destroyed along with the grid,
* which means the mainBody won't be available. On the off chance that the plugin
* isn't destroyed with the grid, take care of removing the listener.
*/
var mainBody = this.grid.getView().mainBody;
if (mainBody) {
mainBody.un('mousedown', this.onMouseDown, this);
}
},

/** @private */
onRowDblClick : function(grid, rowIdx, e) {
this.toggleRow(rowIdx);
},


// This will not get fired for an update
onRowRemoved: function(view, row, rec) {
var panelItemIndex = rec.id;

if (this.expandingRowPanel && this.expandingRowPanel[panelItemIndex]) {
this.expandingRowPanel[panelItemIndex].destroy();
this.expandingRowPanel[panelItemIndex] = null;
}
},

onRowUpdated: function(view, row, rec) {
if (typeof row == 'number') {
row = this.grid.view.getRow(row);
}

this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'collapseRow' : 'expandRow'](row);
},

getBodyContent : function(record, index) {
// extend here
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;
},

onMouseDown : function(e, t) {
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('beforeexpand', this, record, body, rowIndex) !== false) {
if (this.tpl && this.lazyRender) {
body.innerHTML = this.getBodyContent(record, rowIndex);
}
if (body.innerHTML == '' || !this.enableCaching) {
this.createExpandingRowPanel(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');
this.grid.saveState();
this.fireEvent('expand', this, record, body, 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('beforecollapse', this, record, body, row.rowIndex) !== false) {
this.state[record.id] = false;
Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
this.grid.saveState();
this.fireEvent('collapse', this, record, body, row.rowIndex);
}
},

// Expand all rows
expandAll : function() {
var aRows = this.grid.getView().getRows();
for (var i = 0; i < aRows.length; i++) {
this.expandRow(aRows[i]);
}
},

// Collapse all rows
collapseAll : function() {
var aRows = this.grid.getView().getRows();
for (var i = 0; i < aRows.length; i++) {
this.collapseRow(aRows[i]);
}
},

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 = [];
}

// Destroy the existing panel if present
if (this.expandingRowPanel[panelItemIndex]) {
this.expandingRowPanel[panelItemIndex].destroy();
}
this.expandingRowPanel[panelItemIndex] = new Ext.Panel({
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;
}
});

Ext.preg('rowexpander', Ext.ux.grid.RowPanelExpander);


Here is a possible usage of the plugin:


this.expander = new Ext.ux.grid.RowPanelExpander({
createExpandingRowPanelItems: function(record, rowIndex){
var panelItems = [
new Ext.TabPanel({
plain: true,
activeTab: 0,
defaults: {
autoHeight: true
},
record: record,
items:[
{
title:'Info',
listeners: {
activate: function(panel) {
TYPO3.EM.ExtDetails.showExtInfo(panel, panel.ownerCt.record.data);
}
}
},
{
title:'Update',
html: '<div class="loading-indicator">Loading...</div>',
disabled: record.data.installed == 0,
listeners: {
activate: function(panel) {
TYPO3.EM.ExtDirect.getExtensionUpdate(record.data.extkey, function(response) {
panel.update(response);
});
}
}
},
{
title:'Configuration',
disabled: record.data.installed == 0,
},
{
title:'Files',
xtype: 'extfilelist'
},
{
title:'Upload to TER'
},
{
title:'Backup/Delete',
disabled: record.data.installed == 0,
}
]
})
];
return panelItems;
}
});

and it looks like on screenshot.

When using it with stateful grid, don't forget to add expand and collapse to the stateEvents.

have fun!

gregoire
26 Feb 2010, 1:58 AM
Very nice. I can see where this could be very useful.

gurpal2000
26 Feb 2010, 4:45 AM
How do you adapt this to the RowExpander example on the ExtJS 3 examples page?
im seeing:

this.addEvents is not a function
[Break on this error] collapse : true

thanks

steffenk
26 Feb 2010, 4:53 AM
Hi,

maybe you forgot to use it in grids plugins?
this.plugins[this.expander]

I will make a online demo soon.

gurpal2000
26 Feb 2010, 5:00 AM
yes i forgot the 'new' keyword! but when i click + (expand) nothing happens.

heres the code



Ext.onReady(function() {

Ext.QuickTips.init();

var dummyData = [
['3m Co',71.72,0.02,0.03,'9/1 12:00am'],
['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
['The Coca-Cola Company',45.07,0.26,0.58,'9/1 12:00am'],
['The Home Depot, Inc.',34.64,0.35,1.02,'9/1 12:00am'],
['The Procter & Gamble Company',61.91,0.01,0.02,'9/1 12:00am'],
['United Technologies Corporation',63.26,0.55,0.88,'9/1 12:00am'],
['Verizon Communications',35.57,0.39,1.11,'9/1 12:00am'],
['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am'],
['Walt Disney Company (The) (Holding Company)',29.89,0.24,0.81,'9/1 12:00am']
];

// add in some dummy descriptions
for(var i = 0; i < dummyData.length; i++){
dummyData[i].push('Lorem ipsum dolor sit amet, consectetuer adipiscing elit. ');
}

var reader = new Ext.data.ArrayReader({}, [
{name: 'company'},
{name: 'price', type: 'float'},
{name: 'change', type: 'float'},
{name: 'pctChange', type: 'float'},
{name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'},
{name: 'desc'}
]);

var expander = new Ext.ux.grid.RowPanelExpander({
createExpandingRowPanelItems: function(record, rowIndex) {
var panelItems = [
new Ext.TabPanel({
plain: true,
activeTab: 0,
defaults: {
autoHeight: true
},
record: record,
items:[
{
title:'Info'
},
{
title:'Info2'
}
]
})
];
return panelItems;
}
});

var grid = new Ext.grid.GridPanel({
store: new Ext.data.Store({
reader: reader,
data: dummyData
}),
cm: new Ext.grid.ColumnModel([
expander,
{id:'company',header: "Company", width: 40, sortable: true, dataIndex: 'company'},
{header: "Price", width: 20, sortable: true, renderer: Ext.util.Format.usMoney, dataIndex: 'price'},
{header: "Change", width: 20, sortable: true, dataIndex: 'change'},
{header: "% Change", width: 20, sortable: true, dataIndex: 'pctChange'},
{header: "Last Updated", width: 20, sortable: true, renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
]),
viewConfig: {
forceFit:true
},

width: 600,
height: 300,
frame: true,
expander: expander,
title: 'Test row panel expander',
renderTo: document.body
});
});

tonedeaf
26 Feb 2010, 6:05 AM
@gurpal2000
Your code still does not contain a plugins: config for the grid.

gurpal2000
26 Feb 2010, 8:13 AM
d'oh! solved by the below. For some reason i had "expander: expander" in my code



plugins: [ expander ]

steffenk
26 Feb 2010, 4:20 PM
Here is the Online-Demo (http://dev.sk-typo3.de/ext/rowpanelexpander.html)

killfill
28 Feb 2010, 5:08 PM
hey steffenk in the grid of that screenshot you seem to use a GridFilter that is reendered in the grid header. How are you doing that?

Thanks!

steffenk
1 Mar 2010, 1:08 AM
This is the SearchField included in examples/ux, which i changed not to reload the store but to use filterBy.

gurpal2000
4 Mar 2010, 3:18 PM
Hi

I put a grid inside a rowpanelexpander (ie. replaced your tabpanel with gridpanel). I added a rowclick listener to the outer grid. When i click on the rows inside my large grid, i get all sorts of weird behaviour with random rows being highlighted and erratic expand/collapse.

It's hard to explain but if i added a row listener to the inner grid, how would the outer grid know to highlight that large row?



createExpandingRowPanelItems: function(record, rowIndex) {
var panelItems = [
new Ext.grid.GridPanel({
store: new Ext.data.Store({
// blah
}),
...
})
...
]
...

var grid = blah // this uses the rowpanelexpander
grid.on('rowclick', etc)



Thanks

steffenk
4 Mar 2010, 4:54 PM
sounds like you have somehow wrong references or simular ids. I have several components inside the panel, no problems.

daiei27
23 Mar 2010, 9:16 AM
sounds like you have somehow wrong references or simular ids. I have several components inside the panel, no problems.
Nice demo. I wonder if you could switch the tab panels out for grid panels in your demo. That would be the true test of your plugin since that is where most problems arise.

I, for one, had problems similar to Gurpal2000 when using the RowExpander plugin. I'd happily switch to RowPanelExpander if I knew it would work right out of the box so I can get rid of my ugly workarounds.

Btw, thanks for the link from the other thread. ;)

daiei27
23 Mar 2010, 9:25 AM
Oh, 2 questions...

1. Does this plugin re-render the expanded area when closing and opening again? RowExpander seems to do this, meaning my grids are regenerated every time. There are pros (always up to date) and cons (unnecessary processing/delays) to this.

2. Has anyone tried using the "Header for RowExpander" with this plugin?
http://www.extjs.com/forum/showthread.php?t=92958

pavanextjs
9 Apr 2010, 7:55 PM
great Plug-In, Thanks for sharing.

I am trying to use this plug-in in my application, this is awesome and fits into my application needs. But there is a problem when i try to use this with an EditorGridPanel for which i have editors in some of the cells (combo box, Date Field, checkcolumn).

Problem is...
When the expander is in expanded state, and i try to edit one of the cell either by selecting from a combo box or date Field and tab out, the expander panel just disappears and will not be there anymore. Same thing is happening when i try to check/un-check the check column.

I gave the screen shot for the reference.

19849

Any ideas. I tried to debug but no luck. Thanks in advance.http://www.extjs.com/forum/images/misc/pencil.png

steffenk
10 Apr 2010, 7:27 AM
I'm sorry i didn't used it with editor grid before.

Most problems looks like events are catched by the grid. i often use event.stopPropagation() for inside elements to disable event bubbling.

Try to debug events to see what happens, paste following in firebug console:

Ext.util.Observable.prototype.fireEvent = Ext.util.Observable.prototype.fireEvent.createInterceptor(function() {
console.log(arguments);
return true;
});

pavanextjs
21 Apr 2010, 9:53 AM
I'm sorry i didn't used it with editor grid before.

Most problems looks like events are catched by the grid. i often use event.stopPropagation() for inside elements to disable event bubbling.

Try to debug events to see what happens, paste following in firebug console:

Ext.util.Observable.prototype.fireEvent = Ext.util.Observable.prototype.fireEvent.createInterceptor(function() {
console.log(arguments);
return true;
});

Hi Steffen,

Thanks for the quick help and sorry for the late reply, was stuck with other work.

I was able to debug a little bit more by printing the events firing with the above code you have given.

I noticed...

1. 'update' event is firing on grid's store which in turn triggering the Grid View's 'rowupdated' UI event. I am suspecting this 'rowupdated' might be causing the issue by internally updating the row. Because, even though we have many rows in expanded state, only the edited/updated row's expander panel is disappering.

2. 'change' event is firing on the cell editor since we are modifying the data.

3. And there are other events related to 'mouse' are firing.

Can you please help me out more.

Thanks in advance.

pavanextjs
26 Apr 2010, 6:45 AM
The rowupdated event is inturn calling the getRowClass(), which is trying to take the content from the plug-in and failing (It gets nothing from the plug-in) because there is no content as the rowupdated event cleared all the existing content while re-drawing the row.

lingz_public
26 Apr 2010, 10:14 PM
I am also interested in this topic. Has the same problem for grid in expander....

Boxcopter
28 Apr 2010, 12:37 PM
This is exactly what I need for my project, but I can't get toolbars to work within the panels. I am assuming they are not supported or is this a bug?



new Ext.ux.grid.RowPanelExpander({
createExpandingRowPanelItems: function(record, rowIndex) {
var panelItems = [
new Ext.Panel({
height: 200,
autoScroll: true,
record: record,
tbar: new Ext.Toolbar({
items: [
{
xtype: 'tbbutton',
text: 'Edit',
icon: './resources/icons/page_white_edit.png'
}
]
}),
html: record.data.desc
})
]
return panelItems;
}
});
Fixed this problem by adding the toolbar after the panel is created. Here is my code...

var mdlDescExpander = new Ext.ux.grid.RowPanelExpander({
createExpandingRowPanelItems: function(record, rowIndex) {
var text = record.data.desc;
var desc = new Ext.Panel({
layout: 'fit',
autoScroll: true,
maxHeight: 200,
items: new Ext.Panel({
border: false,
frame: false,
autoHeight: true,
autoWidth: true,
autoScroll: true,
html: '<div style="padding:10px" class="clean_style">' + record.data.desc + '</div>'
})
});
desc.elements += ',tbar';
desc.topToolbar = new Ext.Toolbar({
items: [
{
xtype: 'tbtext',
text: 'Description'
},
{
xtype: 'tbtext',
text: '<img src="./resources/icons/line_break.png" />'
},
{
text: 'Open Editor',
icon: './resources/icons/page_white_edit.png',
handler: function() {
openTinyMCE('Model Description Editor', text);
}
}
]
});
var panelItems = [desc]
return panelItems;
}
});

vector3d
7 May 2010, 10:30 AM
I have the same problem.
My case is: a GridPanel in RowExpander of EditorGridPanel if store of "main" EditorGridPanel makes update (after edit or after set data in store) the content of RowExpander disappears...

How to fix this issue? Please Help!!! :((

steffenk
8 May 2010, 2:16 AM
@pavan:
just use the events inside the plugin code. Debug them from there, decide if row is expanded and if you want to let pass them through to parent.

For example look to onMouseDown in plugin as example.

Remember that you are always in a grid, all events of a grid are used, and having grid in grid may cause problems for sure, so you have to take care manually.

Sesshomurai
8 May 2010, 2:11 PM
Here's a little trick I thought up and steffenk helped me with in another thread that is useful here.

Basically, if you want your expanded row panel to be resizable vertically, you can add a resizable bar on the south.



var expander = new Ext.ux.grid.RowPanelExpander({
createExpandingRowPanelItems: function(record, rowIndex){


var panelItems = [
new Ext.TabPanel({
plain: true,
activeTab: 0,
height:'fit',
width:'fit',
defaults: {
autoHeight: true,
autoWidth:true
},
record: record,
listeners: {
render: function() {
var resizer = new Ext.Resizable(this.getEl(), {
handles: 's',
minWidth: 200,
height:100,
minHeight: 100,
maxWidth: 800,
maxHeight: 600,
pinned:true
});

}
},

items:[
{
xtype : 'mediapanel',
id : Ext.id(),
iconCls : 'icon-view',
title:'Quick View',
disableCaching : false,
mediaCfg:{
mediaType :'HTM',
unsupportedText : 'IFrames are not supported by your browser.',
url :"jsp/get-media.jsp?"+wrap+"object="+record.data.data+"&inline=true&repository="+record.data.repository+"&type="+record.data.mime
}
},
{
title:'Related',
iconCls:'icon-reports',
height:'fit'
}
]
})
];
return panelItems;
}
});


Its useful if you are viewing content and so forth in the row.

amsoft
16 May 2010, 10:08 PM
I also interested in this topic. Can you fix the problem with EditorGridPanel, please?

383257357@qq.com
17 May 2010, 2:18 AM
请原谅我不会说英文。我之前也向里面嵌套过一个表格,但是我却无法给每个表格显示不同的数据,这是让我最头痛的一件事。不过还是感谢你的分享。

dawesi
18 May 2010, 5:59 PM
great extension... :-)

mrusinak
19 May 2010, 10:15 AM
I have the same problem.
My case is: a GridPanel in RowExpander of EditorGridPanel if store of "main" EditorGridPanel makes update (after edit or after set data in store) the content of RowExpander disappears...

How to fix this issue? Please Help!!! :((

After some investigating, the problem appears to be (in 3.2.1 anyway) that when rows are removed or re-rendered (such as during an update), the existing rowbody's panel is not removed.

My fix for when a row is updated changes the createExpandingRowPanel function to delete the existing panel and render a new one if an entry for that ID already exists, instead of ignoring creation:

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 done
if (!this.expandingRowPanel) {
this.expandingRowPanel = [];
}

// Destroy the existing panel if present
if (this.expandingRowPanel[panelItemIndex]) {
this.expandingRowPanel[panelItemIndex].destroy();
}
this.expandingRowPanel[panelItemIndex] = new Ext.Panel({
border : false,
bodyBorder : false,
layout : 'fit',
renderTo : rowBody,
items : this.createExpandingRowPanelItems(record, rowIndex)
});
},The fix for row deletion I used was this, also shown with a fix to have the correct expanded/collapsed state after an update:

onRender : function() {
var grid = this.grid;
var mainBody = grid.getView().mainBody;
mainBody.on('mousedown', this.onMouseDown, this, {
delegate : '.x-grid3-row-expander'
});
grid.getView().on('rowremoved', this.onRowRemoved, this);
grid.getView().on('rowupdated', this.onRowUpdated, this);
if (this.expandOnEnter) {
this.keyNav = new Ext.KeyNav(this.grid.getGridEl(),
{
'enter' : this.onEnter,
scope : this
});
}
if (this.expandOnDblClick) {
grid.on('rowdblclick', this.onRowDblClick, this);
}
},

// This will not get fired for an update
onRowRemoved: function(view, row, rec) {
var panelItemIndex = rec.id;

if (this.expandingRowPanel && this.expandingRowPanel[panelItemIndex]) {
this.expandingRowPanel[panelItemIndex].destroy();
this.expandingRowPanel[panelItemIndex] = null;
}
},

onRowUpdated: function(view, row, rec) {
if (typeof row == 'number') {
row = this.grid.view.getRow(row);
}

this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'collapseRow' : 'expandRow'](row);
},

amsoft
19 May 2010, 9:52 PM
Thanks a looooot=)

youngqj
19 May 2010, 10:11 PM
Great! Thanks for share!

innerbreath
26 May 2010, 8:00 AM
Hi,

I am trying to use the RowPanelexpander. It works in FF with grid.autoheight:true but not in IE.

Any help would be greatly appreciated.

Thanks,

Kamal.

prophet
27 May 2010, 12:25 PM
Great! Thank you!

pavanextjs
2 Jun 2010, 8:01 AM
Hi mrusinak,

The solution looks perfect. I did the destroy part in my application, but unable to figure out the rowremoved & rowupdated part. I will try that now.

Thank you so much life saver.

walker_cn
6 Jun 2010, 6:37 PM
great Plug-In, Thanks for sharing.

But there is a problem. I test in your demo, For any row, when the expander is in expanded state, and i try to click the column title, the grid sort data, and the expander panel just disappears and will not be there anymore.

Sorry for my english, you can test your demo:
1. click a row, to expand the expander panel
2. click column title to sort
3. click same row, but the expander panel never appear

piccard
8 Jun 2010, 7:14 AM
Hello,
great extension and a great bug-fix. thanx a lot ;-)
.. but I recognized, that it's not possible to render any form-buttons inside the demo-tab.
anybody an idea???

pavanextjs
8 Jun 2010, 7:33 AM
walker_cn,

The fix suggested by mrusinak above will fix this issue. Please refer to his code snippet above.

Thanks,
Pavan.

pavanextjs
8 Jun 2010, 7:46 AM
piccard,

We can insert any ExtJS Components inside the panel. I have a Tree, List View and button in my expander panel.

piccard
8 Jun 2010, 8:44 AM
Hello pavanextjs,
that's sounds good. I also took the demo of the author and put a form inside, but it's the same.
Even inside the demo the buttons are not rendered.
If u know a workaround or u see an error by myself, please inform me.
Have a look.


Ext.onReady(function(){

Ext.QuickTips.init();

var xg = Ext.grid;

var expander = new Ext.ux.grid.RowPanelExpander({
createExpandingRowPanelItems: function(record, rowIndex){
var panelItems = [
new Ext.TabPanel({
plain: true,
activeTab: 0,
defaults: {
autoHeight: true
},
record: record,
items:[
{
title:'Info1',
html: '<div id=\'aaa\'></div>',
listeners: {
activate: function(panel) {
var chartForm = new Ext.FormPanel({
//renderTo: 'chartFormDiv_'+record.data.volume,
id: 'chartForm',
labelWidth: 125,
//bodyBorder: false,
//border: false,
frame: true,
title: 'Date Range',
bodyStyle:'padding:5px 5px 0; border: none', // here we need border 0px, otherwise we get a bodyborder of 1px (bodyborder: false doesn't work with frame: true
width: 330,
//defaultType: 'datefield',
items: [{
xtype:'fieldset',
title: 'Phone Number',
//checkboxToggle:true,
collapsible: true,
autoHeight:true,
defaults: {width: 210},
defaultType: 'textfield',
items :[{
fieldLabel: 'Home',
name: 'home',
value: '(888) 555-1212'
},{
fieldLabel: 'Business',
name: 'business',
emptyText: 'blah blah blah',
},{
fieldLabel: 'Mobile',
name: 'mobile'
},{
fieldLabel: 'Fax',
name: 'fax'
}
]

}],

buttons: [{
text: 'Save',
id: 'kashdk'
},{
text: 'Cancel',
id: 'aopsduah'
}]
});
chartForm.render('aaa');
}}
},
{
title:'Info2',
html: '<h2>askdjhakjsdhakj</h2><p>Background Informations</p>'
}
]
})
];
return panelItems;
}
});

// shared reader
var reader = new Ext.data.ArrayReader({}, [
{name: 'company'},
{name: 'price', type: 'float'},
{name: 'change', type: 'float'},
{name: 'pctChange', type: 'float'},
{name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'},
{name: 'industry'},
{name: 'desc'}
]);
var store = new Ext.data.GroupingStore({
reader: reader,
data: xg.dummyData,
sortInfo:{field: 'company', direction: "ASC"},
groupField:'industry'
});

var grid = new xg.GridPanel({
store: store,
columns: [
expander,
{id:'company',header: "Company", width: 60, sortable: true, dataIndex: 'company'},
{header: "Price", width: 20, sortable: true, renderer: Ext.util.Format.usMoney, dataIndex: 'price'},
{header: "Change", width: 20, sortable: true, dataIndex: 'change', renderer: Ext.util.Format.usMoney},
{header: "Industry", width: 20, sortable: true, dataIndex: 'industry'},
{header: "Last Updated", width: 20, sortable: true, renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
],

view: new Ext.grid.GroupingView({
forceFit:true,
groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
}),

plugins: [expander],

frame:true,
width: 700,
height: 450,
collapsible: true,
animCollapse: false,
title: 'Grouping Example',
iconCls: 'icon-grid',
fbar : ['->', {
text:'Clear Grouping',
iconCls: 'icon-clear-group',
handler : function(){
store.clearGrouping();
}
}],
renderTo: document.body
});
});
// Array data for the grids
Ext.grid.dummyData = [
['3m Co',71.72,0.02,0.03,'4/2 12:00am', 'Manufacturing'],
['Alcoa Inc',29.01,0.42,1.47,'4/1 12:00am', 'Manufacturing'],
['Altria Group Inc',83.81,0.28,0.34,'4/3 12:00am', 'Manufacturing'],
['American Express Company',52.55,0.01,0.02,'4/8 12:00am', 'Finance'],
['American International Group, Inc.',64.13,0.31,0.49,'4/1 12:00am', 'Services'],
['AT&T Inc.',31.61,-0.48,-1.54,'4/8 12:00am', 'Services'],
['Boeing Co.',75.43,0.53,0.71,'4/8 12:00am', 'Manufacturing'],
['Caterpillar Inc.',67.27,0.92,1.39,'4/1 12:00am', 'Services'],
['Citigroup, Inc.',49.37,0.02,0.04,'4/4 12:00am', 'Finance'],
['E.I. du Pont de Nemours and Company',40.48,0.51,1.28,'4/1 12:00am', 'Manufacturing'],
['Exxon Mobil Corp',68.1,-0.43,-0.64,'4/3 12:00am', 'Manufacturing'],
['General Electric Company',34.14,-0.08,-0.23,'4/3 12:00am', 'Manufacturing'],
['General Motors Corporation',30.27,1.09,3.74,'4/3 12:00am', 'Automotive'],
['Hewlett-Packard Co.',36.53,-0.03,-0.08,'4/3 12:00am', 'Computer'],
['Honeywell Intl Inc',38.77,0.05,0.13,'4/3 12:00am', 'Manufacturing'],
['Intel Corporation',19.88,0.31,1.58,'4/2 12:00am', 'Computer'],
['International Business Machines',81.41,0.44,0.54,'4/1 12:00am', 'Computer'],
['Johnson & Johnson',64.72,0.06,0.09,'4/2 12:00am', 'Medical'],
['JP Morgan & Chase & Co',45.73,0.07,0.15,'4/2 12:00am', 'Finance'],
['McDonald\'s Corporation',36.76,0.86,2.40,'4/2 12:00am', 'Food'],
['Merck & Co., Inc.',40.96,0.41,1.01,'4/2 12:00am', 'Medical'],
['Microsoft Corporation',25.84,0.14,0.54,'4/2 12:00am', 'Computer'],
['Pfizer Inc',27.96,0.4,1.45,'4/8 12:00am', 'Services', 'Medical'],
['The Coca-Cola Company',45.07,0.26,0.58,'4/1 12:00am', 'Food'],
['The Home Depot, Inc.',34.64,0.35,1.02,'4/8 12:00am', 'Retail'],
['The Procter & Gamble Company',61.91,0.01,0.02,'4/1 12:00am', 'Manufacturing'],
['United Technologies Corporation',63.26,0.55,0.88,'4/1 12:00am', 'Computer'],
['Verizon Communications',35.57,0.39,1.11,'4/3 12:00am', 'Services'],
['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'4/3 12:00am', 'Retail'],
['Walt Disney Company (The) (Holding Company)',29.89,0.24,0.81,'4/1 12:00am', 'Services']
];

// add in some dummy descriptions
for(var i = 0; i < Ext.grid.dummyData.length; i++){
Ext.grid.dummyData[i].push('Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed metus nibh, sodales a, porta at, vulputate eget, dui. Pellentesque ut nisl. Maecenas tortor turpis, interdum non, sodales non, iaculis ac, lacus. Vestibulum auctor, tortor quis iaculis malesuada, libero lectus bibendum purus, sit amet tincidunt quam turpis vel lacus. In pellentesque nisl non sem. Suspendisse nunc sem, pretium eget, cursus a, fringilla vel, urna.<br/><br/>Aliquam commodo ullamcorper erat. Nullam vel justo in neque porttitor laoreet. Aenean lacus dui, consequat eu, adipiscing eget, nonummy non, nisi. Morbi nunc est, dignissim non, ornare sed, luctus eu, massa. Vivamus eget quam. Vivamus tincidunt diam nec urna. Curabitur velit.');
}

pavanextjs
8 Jun 2010, 9:16 AM
Try bbar instead of buttons config...


bbar: [{
text: 'Save',
id: 'kashdk'
},{
text: 'Cancel',
id: 'aopsduah'
}]

piccard
8 Jun 2010, 11:58 PM
thanx for the bbar-tip, pavanextjs, it's working.
you made me happy ;-)

nevertheless, I think it's really a problem, not rendering any buttons inside a form :-(

boriss
22 Aug 2010, 11:41 AM
I put a grid inside a rowpanelexpander (ie. replaced your tabpanel with gridpanel). I added a rowclick listener to the outer grid. When i click on the rows inside my large grid, i get all sorts of weird behaviour with random rows being highlighted and erratic expand/collapse.

It's hard to explain but if i added a row listener to the inner grid, how would the outer grid know to highlight that large row?
Quite a lot of events bubble up. This includes eg. mouseover and mouseout. When you move the mouse over an inner grid rows of the outer grid are also highlighted. The appropriate event handlers in Ext.grid.GridView must be overridden to prevent this:


Ext.override(Ext.grid.GridView, {
onRowOver: function(e, t) {
var row;
if ((row = this.findRowIndex(t)) !== false) {
this.addRowClass(row, 'x-grid3-row-over');
}
e.stopEvent();
},

onRowOut: function(e, t) {
var row;
if ((row = this.findRowIndex(t)) !== false && !e.within(this.getRow(row), true)) {
this.removeRowClass(row, 'x-grid3-row-over');
}
e.stopEvent();
}
});

Other events like click and doubleclick are handled by the GridPanel. Only one function must be overridden though as it is used by all event handlers in that class:



Ext.override(Ext.grid.GridPanel, {
processEvent: function(name, e) {
this.view.processEvent(name, e);
e.stopEvent();
}
}

As far as I can tell my inner grid now doesn't interfere with the outer grid anymore.

Condor
22 Aug 2010, 9:43 PM
You shouldn't need to stop the event if you configure your cellSelectorDepth, rowSelectorDepth and rowBodySelectorDepth correctly.

Animal
22 Aug 2010, 11:43 PM
Your onDestroy needs to destroy all the cached Panels.

boriss
23 Aug 2010, 1:38 PM
Your onDestroy needs to destroy all the cached Panels.

You mean the ones in this.expandingRowPanel? If this is required it should actually be done by onDestroy() in RowPanelExpander (so the class can clean up its own cached panels)?

boriss
23 Aug 2010, 2:07 PM
You shouldn't need to stop the event if you configure your cellSelectorDepth, rowSelectorDepth and rowBodySelectorDepth correctly.

I tried it but unfortunately it doesn't work. But looking at the code in GridView I'm not sure either how these settings are relevant to event bubbling?

Animal
23 Aug 2010, 9:36 PM
Yes.

Condor
23 Aug 2010, 9:43 PM
They are not relevant to event bubbling, but if a cell or row is not found within the specified depth the event is not treated as a cell/row event.

boriss
24 Aug 2010, 11:55 AM
They are not relevant to event bubbling, but if a cell or row is not found within the specified depth the event is not treated as a cell/row event.

I don't understand how the part "if a cell or row is not found" applies here. For example if I move the mouse pointer over some rows in an inner grid the very same rows in the outer grid are highlighted. Which cells or rows are not found here that you think setting cellSelectorDepth, rowSelectorDepth and rowBodySelectorDepth helps?

boriss
24 Aug 2010, 1:46 PM
Does anyone know how to set the height of an inner grid? If I pass eg. "height: 300" to the inner grid and open the row expander there is a grid with that height. However it's empty (I see a white empty box now). If I don't set the height everything works automatically (rows are loaded and height is somehow automatically calculated).

The reason why I want to set the height is that some of my inner grids indeed have no rows. As I provide a button to add rows the inner grid should have a minimum height (otherwise a row is inserted but you can't see nor edit it).

boriss
30 Aug 2010, 2:25 PM
Does anyone know how to set the height of an inner grid? If I pass eg. "height: 300" to the inner grid and open the row expander there is a grid with that height. However it's empty (I see a white empty box now). If I don't set the height everything works automatically (rows are loaded and height is somehow automatically calculated).

Meanwhile I can refine the problem: Setting height of an inner grid basically works. What does not work is: Top and bottom toolbars (tbar and bbar) are not displayed. There is some space allocated but it's empty (white). Looking around in the debugger I see that the toolbars' width is set to 0. The divs with the classes "x-panel-tbar x-panel-tbar-noheader" and "x-toolbar x-small-editor x-toolbar-layout-ct" have both a width of 0px. If I change the width in the debugger the toolbars become visible. Somewhere the width is wrongly calculated. This only happens in the toolbars though - the panel's body has the expected width.

I'll continue debugging tomorrow. I appreciate any ideas of what could be wrong though. For example where do toolbars get the width from? It doesn't seem to be set in the render functions in Ext.Toolbar. Is it done in Ext.grid.GridPanel or Ext.grid.GridView?

boriss
18 Oct 2010, 3:20 PM
I'm finally able to describe the problem exactly and propose a fix. Have a look at this pseudocode:

var expander = new Ext.ux.grid.RowPanelExpander({
createExpandingRowPanelItems: function(record, rowIndex) {
return [
new Ext.grid.GridPanel({
height: 100,
tbar: new Ext.Toolbar(...),
bbar: new Ext.Toolbar(...)
})
]
}
});
When a grid panel is embedded in the row panel expander and a height is set, no top and bottom bars will be displayed. The reason is that somewhere in the grid panel when the toolbars are resized the offsetWidth of the grid panel is read. However this all happens before the row panel is actually expanded and visible - which means offsetWidth is 0!

Here are the details:

RowPanelExpander calls in expandRow() beforeExpand() which again calls createExpandingRowPanel(). Here components like our GridPanel are created. expandRow() calls beforeExpand() though before this line is executed:

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

Only then the row panel becomes visible (and reading attributes like offsetWidth makes sense).

Why is this a problem with toolbars in a grid panel? onRender() in GridPanel calls afterRender(). The call is forwarded a few times to afterRender() in BoxComponent (a parent class). BoxComponent calls setSize() to change the height of the grid panel (we set a height of 100). As a dimension is set explicitly onResize() gets called. And onResize() in Panel (another parent class of GridPanel) wants to set the toolbars' width. As it needs to use a value it reads this.body.dom.offsetWidth (line 1483 in Panel.js). But as nothing is visible yet offsetWidth is 0 and the width of the toolbars set to 0.

The quick fix is to move the call to replaceClass() in expandRow() in RowPanelExpander before beforeExpand(). Then any components which need to read attributes like offsetWidth get reasonable values and can be outlined correctly.

paubach
22 Nov 2010, 3:46 AM
great Plug-In, Thanks for sharing.

But there is a problem. I test in your demo, For any row, when the expander is in expanded state, and i try to click the column title, the grid sort data, and the expander panel just disappears and will not be there anymore.

Sorry for my english, you can test your demo:
1. click a row, to expand the expander panel
2. click column title to sort
3. click same row, but the expander panel never appear


It happens to me too. Adding the fix, when sorting, the expanded panel disappears, although we can later collapse and expand and it appears again. But it would be great if this wouldn't disappear.

Juel
2 Dec 2010, 2:51 AM
The quick fix is to move the call to replaceClass() in expandRow() in RowPanelExpander before beforeExpand(). Then any components which need to read attributes like offsetWidth get reasonable values and can be outlined correctly.Hi, I guess I have a similar problem in a different context...


createExpandingRowPanelItems: function(record, rowIndex){
var testpanel = new Ext.Panel({
html:'Teeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeest',
autoScroll:true,
autoWidth:true
});
var panelItems = [testpanel];
return panelItems;
}My panel doesn't show a scrollbar and teeest - text gets cut at the right end of the panel.
I moved replaceClass() from expandRow() to the first line in beforeExpand(), but didn't get any different behaviour.

daiei27
3 Dec 2010, 7:54 AM
Guess after I posted in this thread I never came back and mentioned I updated the original RowExpander. I don't promise it'll fit everybody's needs, but it can contain all sorts of Ext elements within it without creating memory leaks and I haven't had any of the issues mentioned in this thread.

You can give it a try to see if it fits your purposes better:
http://www.sencha.com/forum/showthread.php?96245-3.x-3.2.0-RowExpander-plugin-UPDATED-April-5-2010

paubach
15 Dec 2010, 7:51 AM
Guess after I posted in this thread I never came back and mentioned I updated the original RowExpander. I don't promise it'll fit everybody's needs, but it can contain all sorts of Ext elements within it without creating memory leaks and I haven't had any of the issues mentioned in this thread.

You can give it a try to see if it fits your purposes better:
http://www.sencha.com/forum/showthread.php?96245-3.x-3.2.0-RowExpander-plugin-UPDATED-April-5-2010

The same error that I posted about RoxPanelExpander happens with RowExpander. But in that case 1st change (sorting or anything) makes the content of the expander to disappear leaving an empty box, and 2nd change the box disappears. Everything very weird...

steffenk
23 Dec 2010, 5:18 AM
I updated first post code and added support for stateful grid, added known issues from this thread and 2 new methods: expandAll and collapseAll

Juel
27 Dec 2010, 10:46 AM
Hi steffenk, thats cool - I tried the new RowPanelExpander but still have this (http://www.sencha.com/forum/showthread.php?92994-RowPanelExpander-for-grids&p=545898#post545898) issue and also sorting doesn't seem to work as expected, like paubach stated before... did you test that in detail? Otherwise I do something completely wrong :(

steffenk
28 Dec 2010, 1:06 PM
Hi,

i will look to the scroll issue. I use it stateful and with different types of containers. This is built in with TYPO3 4.5beta3 and works pretty well.

24000

steffenk
3 Jan 2011, 3:46 AM
Just an update: with remote grid and stateful it works after reload/sort. problem is the local mode, i'm currently work on a solution.

Juel
4 Jan 2011, 6:29 AM
I used it with a local store and not stateful, that made the difference probably... looking forward to the local version ;-)

pavanextjs
10 Jan 2011, 5:08 PM
I have this strange problem with the CheckboxSelectionModel inside the nested grid of RowPanelExpander.

Problem is explained in this Link (http://www.sencha.com/forum/showthread.php?120842-Problem-with-CheckboxSelectionModel-inside-the-Nested-Grid-of-RowPanelExpander&p=559451#post559451).

Any ideas & suggestions ?

MrSparks
5 Jun 2011, 1:29 PM
@Steffen,

Do you have any plans to migrate this UX to 4.x? I've done a lot of searching around and your UX was the one to have when 3.x was current.

davidryan
16 Nov 2011, 9:46 AM
@Steffenk

Any chance you would be willing to update this excellent plugin to support ExtJS4? (including editable grids)

cmadison0005
13 Mar 2012, 7:54 AM
Where is this function defined? Or is it part of a superclass?

This line breaks my code everytime i try to incorporate the PanelRowExpander plugin

Any ideas? I have included the constructor code below


constructor: function(config)
{
Ext.apply(this,config);

ux.grid.DocumentRowExpander.superclass.superclass.constructor.call(this, config);

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

this.state = {};

this.expandingRowPanels = {};
},,

and the init function:

init : function(grid) // INIT function
{
console.log('Within plugin init');
this.grid = grid;

var view = grid.getView();
// view.getRowClass = this.getRowClass.createDelegate(this);
view.getRowClass = Ext.Function.bind.apply(Ext.Function, arguments);
view.enableRowBody = true;

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

this.relayEvents(grid, ['resize']);

// 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 = {};
for (var id in this.expandingRowPanels) {
this.expandingRowPanels[id].destroy();
}
this.expandingRowPanels = {};
}, this);

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


UPDATE: I found the ExtJS4 analog to createDelegate and have replaced it in the post below.

cmadison0005
14 Mar 2012, 5:51 AM
Im working within an ExtJS 4 MVC acrhitecture and would like to get this plugin functioning so that i can show a child gridpanel with the expansion of a row.

I added:

Ext.require(['ux.grid.DocumentRowExpander']);
above the definition of the grid panel which will contain the plugin. Secondly, i added the following to said grid panels config:

plugins: [{
ptype: 'documentrowexpander',
pluginId: 'document_row_expander'
}],


I have attached DocumentRowExpander.js below, which is my attempt at applying this plugin to the situation described above.


Ext.define('ux.grid.DocumentRowExpander', {
extend: 'Ext.util.Observable',
alias: 'plugin.documentrowexpander',
// config items for rowExpander
expandOnEnter: true,
expandOnDblClick: true,
header: '',
width: 20,
sortable: false,
fixed: true,
menuDisabled: true,
dataIndex: '',
lazyRender: true,
enableCaching: true,

constructor: function(config)
{
console.log('plugin constructor');
Ext.apply(this,config);
this.addEvents( {
/**
* @event beforeexpand
* Fires before the row expands. Have the listener return false to prevent the row from expanding.
* @param {Object} this RowExpander object.
* @param {Object} Ext.data.Record Record for the selected row.
* @param {Object} body body element for the secondary row.
* @param {Number} rowIndex The current row index.
*/
beforeexpand : true,
/**
* @event expand
* Fires after the row expands.
* @param {Object} this RowExpander object.
* @param {Object} Ext.data.Record Record for the selected row.
* @param {Object} body body element for the secondary row.
* @param {Number} rowIndex The current row index.
*/
expand : true,
/**
* @event beforecollapse
* Fires before the row collapses. Have the listener return false to prevent the row from collapsing.
* @param {Object} this RowExpander object.
* @param {Object} Ext.data.Record Record for the selected row.
* @param {Object} body body element for the secondary row.
* @param {Number} rowIndex The current row index.
*/
beforecollapse : true,
/**
* @event collapse
* Fires after the row collapses.
* @param {Object} this RowExpander object.
* @param {Object} Ext.data.Record Record for the selected row.
* @param {Object} body body element for the secondary row.
* @param {Number} rowIndex The current row index.
*/
collapse : true
});

ux.grid.DocumentRowExpander.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= {};
},

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) {
console.log('Plugin init');
this.grid = grid;
var view = grid.getView();
// view.getRowClass = this.getRowClass.createDelegate(this);//(ORIGINAL)
// view.getRowClass = Ext.createDelegate(this);
view.getRowClass = Ext.bind(this.getRowClass, this, arguments); // (My attempt for 4.0)

view.enableRowBody = true;
grid.on('render', this.onRender, this);
view.on('refresh', this.onViewRefresh, this);
grid.on('destroy', this.onDestroy, this);
grid.on("beforestaterestore", this.applyState, this);
grid.on("beforestatesave", this.saveState, this);
},

// @private
onRender : function() {
console.log('Plugin onRender');
var grid = this.grid;
//var mainBody = grid.getView().mainBody; // ORIGINAL
var mainBody = grid.getView().getEl(); // (My attempt for 4.0)
mainBody.on('mousedown', this.onMouseDown, this, {
delegate : '.x-grid3-row-expander'
});

grid.getView().on('rowremoved', this.onRowRemoved, this);
grid.getView().on('rowupdated', this.onRowUpdated, this);

if (this.expandOnEnter) {
this.keyNav = new Ext.KeyNav(this.grid.getEl(),
{
'enter' : this.onEnter,
scope : this
});
}
if (this.expandOnDblClick) {
grid.on('rowdblclick', this.onRowDblClick, this);
}
},

/** @private */
onViewRefresh: function(view) {
var grid = view.grid;
var index = -1;
for(var key in this.state){
if (this.state[key] === true) {
index = grid.getStore().indexOfId(key);
if (index > -1) {
this.expandRow(index);
}
}
}
},

/** @private */
applyState: function(grid, state){
this.suspendStateStore = true;
if(state[this.id]) {
this.state = state[this.id];
}
this.suspendStateStore = false;
},

/** @private */
saveState: function(grid, state){
return state[this.id] = this.state;
},

/** @private */
onDestroy : function() {
if (this.keyNav) {
this.keyNav.disable();
delete this.keyNav;
}
/*
* A majority of the time, the plugin will be destroyed along with the grid,
* which means the mainBody won't be available. On the off chance that the plugin
* isn't destroyed with the grid, take care of removing the listener.
*/
var mainBody = this.grid.getView().mainBody;
if (mainBody) {
mainBody.un('mousedown', this.onMouseDown, this);
}
},

/** @private */
onRowDblClick : function(grid, rowIdx, e) {
this.toggleRow(rowIdx);
},

// This will not get fired for an update
onRowRemoved: function(view, row, rec) {
var panelItemIndex = rec.id;

if (this.expandingRowPanel && this.expandingRowPanel[panelItemIndex]) {
this.expandingRowPanel[panelItemIndex].destroy();
this.expandingRowPanel[panelItemIndex] = null;
}
},

onRowUpdated: function(view, row, rec) {
if (typeof row == 'number') {
row = this.grid.view.getRow(row);
}

this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'collapseRow' : 'expandRow'](row);
},

getBodyContent : function(record, index) {
// extend here
if (!this.enableCaching) {
return this.tpl.apply(record.data);
}
var content = this.bodyContent[record.id];
if (!content) {
if (this.tpl) {
content = this.tpl.apply(record.data);
this.bodyContent[record.id] = content;
}
}
return content;
},

onMouseDown : function(e, t) {
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('beforeexpand', this, record, body, rowIndex) !== false) {
if (this.tpl && this.lazyRender) {
body.innerHTML = this.getBodyContent(record, rowIndex);
}
if (body.innerHTML == '' || !this.enableCaching) {
this.createExpandingRowPanel(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);
this.grid.saveState();
},

expandRow : function(row) {
if (typeof row == 'number') {
row = this.grid.view.getRow(row);
}
if (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');
this.grid.saveState();
this.fireEvent('expand', this, record, body, 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('beforecollapse', this, record, body, row.rowIndex) !== false) {
this.state[record.id] = false;
Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
this.grid.saveState();
this.fireEvent('collapse', this, record, body, row.rowIndex);
}
},

// Expand all rows
expandAll : function() {
var aRows = this.grid.getView().getRows();
for (var i = 0; i < aRows.length; i++) {
this.expandRow(aRows[i]);
}
},

// Collapse all rows
collapseAll : function() {
var aRows = this.grid.getView().getRows();
for (var i = 0; i < aRows.length; i++) {
this.collapseRow(aRows[i]);
}
},

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 = [];
}

// Destroy the existing panel if present
if (this.expandingRowPanel[panelItemIndex]) {
this.expandingRowPanel[panelItemIndex].destroy();
}
this.expandingRowPanel[panelItemIndex] = new Ext.Panel({
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;
}
});

When i actually try to include the plugin in the grid panel, i get an error telling me that method.apply (from ext-debug.js) is not a function. Any one care to enlighten me a bit?

Thanks

tdikarim
22 Sep 2012, 6:39 AM
Hi,

This plugin seems great !!

Is someone have ported this plugin on Extjs4 with success ?

Thanks
Karim

ajit.mankottil
27 Nov 2012, 7:37 PM
Hi,
I am adding form panel into row panel expander but I got following error -:

"htmlfile: Unknown runtime error"

If I added field-set directly into tab, it is showing but rendering problem. Grid's width is very small.

Please let's me know how I can add form panel into RowPanelExpander.

Thanks,
Alex



var expander = new Ext.ux.grid.RowPanelExpander({
createExpandingRowPanelItems: function (record, rowIndex) {


outportStore = new Ext.data.JsonStore({
loadMask: false,
autoLoad: false,
autoDestroy: true,
root: 'result',
fields: ['BusinessUnitId', 'Name', 'IsSelected'],
idProperty: 'BusinessUnitId'
});


outportStore.loadData({ "result": record.data.ContractOutportsDtos });


var chkConducted = new Ext.grid.CheckColumn({
header: '',
id: 'chkPost',
dataIndex: 'IsSelected',
width: 1
});


var outportColModel = new Ext.grid.ColumnModel([
new Ext.grid.RowNumberer(),
chkConducted, {
header: "Outport",
width: 10,
sortable: true,
dataIndex: 'Name'
}]);


var grdoutport = new Ext.grid.EditorGridPanel({
store: outportStore,
colModel: outportColModel,
plugins: [chkConducted],
frame: true,
loadMask: false,
viewConfig: {
forceFit: true
},
style: 'margin:5px 0 5px 0',
autoHeight: true,
autoWidth: true,
columnLines: true,
enableHdMenu: false,


sm: new Ext.grid.RowSelectionModel({
singleSelect: true
}),
clicksToEdit: 1,
anchor: '90%'
});


var txtSubmittedByName = {
xtype: 'textfield',
fieldLabel: 'Submitted By Name',
id: 'txtSubmittedByName',
name: 'name',
anchor: '90%'
};


var fsOutports = {
xtype: 'fieldset',
columnWidth: 1,
title: 'Contract Mapping with Outports',
collapsible: false,
autoHeight: true,
margins: '5 5 5 5',
bodyStyle: {
padding: '10px 20px'
},
layout: 'column',
items: [{
columnWidth: 1,
border: false,
items: [txtSubmittedByName]
}, {
columnWidth: 0.40,
border: false,
items: [grdoutport]
}]
};


var formPanel = new Ext.FormPanel({
layout: 'anchor',
bodyStyle: {
padding: '10px 20px'
},
items: [fsOutports]


});


var tabPanel = new Ext.TabPanel({
activeTab: 0,
deferredRender: false,
items: [{
title: 'Testing',
items: [formPanel]
}]
});

return [tabPanel];
}
});


If I added fiedset directly into tab, it is working but not renderer correctly. Please see the attached image.


var tabPanel = new Ext.TabPanel({
activeTab: 0,
deferredRender: false,
items: [{
title: 'Testing',
items: [fsOutports]
}]
});


40399

ajit.mankottil
22 Jan 2013, 10:27 PM
Hi,
I have added the gird into RowPanelExpender. It is working as expected but the is layout issue in grid.
Grid Header alignment is mismatch with row. So I look at the source in IE and i have found the problem.


The table width of the Header is 20px more than row table width.
That 20px width is for scroll bar. The scroll bar width is missing on header but it is render in row table.


Could you please let me know how to solved this issue.


Thanks,
Alex