View Full Version : Prevent listener function expansion on row collapse

7 Oct 2013, 1:18 AM
I am using Ext Js 4.2.2 and i developing a grid that includes a row expansion.

In row expansion i used a listener "itemdblclick". When anyone double click on a row the row will expand and get the values from database and i am showing those values. Upto this perfect.

If i want to close the expansion of the row , i am again double click on that row and the row expansion collapses well but fires the listener again and one call is going to database to get values.This call to database is not required when collapsing the row expansion on double click.

Suggest me event or trick to achieve this

7 Oct 2013, 2:38 AM
did you try event.stopEvent() ?

7 Oct 2013, 2:51 AM
If i do that when i want to expand also that will not work right.
I am doing expansion code itemdblclck listener , if i do your thing then expansion also do not work it seems

7 Oct 2013, 3:00 AM
Hmm without some code i'm afraid i can't help you any further than that, as that's what i would normally do.

listeners: {
itemdblclick: function( el, record, item, index, e, eOpts ){
// your other code, e.g. expand your row or collapse it, depending on it's current state

7 Oct 2013, 4:28 AM
Thanks for the reply

Here is my code

listeners : {
itemdblclick : function(dv, record, item, index, e,
rowid) {
expandrow(index, record);

expandrow is the function which triggers to Database.
When i hit double click expansion works fine and it is going to database and getting values.

If i again hit double click the expansion is closed but again database call is happening. This should not happen. Only it should collapse the row expansion , but call to the database should not happen. This is what i am looking for

7 Oct 2013, 6:08 AM
I've just looked it up in the sencha docs, because i was wondering that there ain't a direct way to detect row expansion / collapsing as with all other components that support such actions, and i found those:
- expandbody( rowNode, record, expandRow, eOpts )
- collapsebody( rowNode, record, expandRow, eOpts )

The events are triggered when expanded/collapsed. For your purposes it should be enough to use the expandbody event.
According to the docs you simply supply these listeners within the grids listeners configuration, at least thats what i understand when i read it:
"Fired through the grid's View."

Have a go and test it ;)

If you wanna keep your code and just edit it to not execute a database query, then supply a variable that identifies the current status of your row (expanded/collapsed) and act accordingly. Maybe there is even a pre-defined way to detect if your row is already expanded. You should look up your rows properties with Firebug or Chrome-Dev-Tools.

7 Oct 2013, 8:32 PM
No it will not work because i am using customrowexpander. Here is the code for customrowexpander.
That does not have functions like expandbody,collapsebody

extend : 'Ext.AbstractPlugin',
lockableScope : 'normal',
alias : 'plugin.customrowexpander',

rowBodyTpl : null,

* @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,

* @cfg {Boolean} selectRowOnExpand <tt>true</tt> to
* select a row when clicking on the expander icon
* (defaults to <tt>false</tt>).
selectRowOnExpand : false,

rowBodyTrSelector : '.x-grid-rowbody-tr',
rowBodyHiddenCls : 'x-grid-row-body-hidden',
rowCollapsedCls : 'x-grid-row-collapsed',

addCollapsedCls : {
before : function(values, out) {
var me = this.rowExpander;
if (!me.recordsExpanded[values.record.internalId]) {
priority : 500

* @event expandbody **Fired through the grid's View**
* @param {HTMLElement}
* rowNode The &lt;tr> element which owns the
* expanded row.
* @param {Ext.data.Model}
* record The record providing the data.
* @param {HTMLElement}
* expandRow The &lt;tr> element containing the
* expanded data.
* @event collapsebody **Fired through the grid's View.**
* @param {HTMLElement}
* rowNode The &lt;tr> element which owns the
* expanded row.
* @param {Ext.data.Model}
* record The record providing the data.
* @param {HTMLElement}
* expandRow The &lt;tr> element containing the
* expanded data.

setCmp : function(grid) {
var me = this, rowBodyTpl, features;


me.recordsExpanded = {};
if (!me.rowBodyTpl) {
.raise("The 'rowBodyTpl' config is required and is not defined.");

me.rowBodyTpl = Ext.XTemplate.getTpl(me, 'rowBodyTpl');
rowBodyTpl = this.rowBodyTpl;
features = [
ftype : 'rowbody',
lockableScope : 'normal',
recordsExpanded : me.recordsExpanded,
rowBodyHiddenCls : me.rowBodyHiddenCls,
rowCollapsedCls : me.rowCollapsedCls,
setupRowData : me.getRowBodyFeatureData,
setup : me.setup,
getRowBodyContents : function(record) {
return rowBodyTpl.applyTemplate(record
}, {
ftype : 'rowwrap',
lockableScope : 'normal'
} ];

if (grid.features) {
grid.features = Ext.Array.push(features,
} else {
grid.features = features;
// NOTE: features have to be added before init (before
// Table.initComponent)

init : function(grid) {
var me = this, reconfigurable = grid, view, lockedView;

me.grid = grid;
view = me.view = grid.getView();
// Columns have to be added in init (after columns has
// been used to create the headerCt).
// Otherwise, shared column configs get corrupted, e.g.,
// if put in the prototype.
// me.addExpander();

// Bind to view for key and mouse events
// Add row processor which adds collapsed class
view.addRowTpl(me.addCollapsedCls).rowExpander = me;

// If the owning grid is lockable, then disable row
// height syncing - we do it here.
// Also ensure the collapsed class is applied to the
// locked side by adding a row processor.
if (grid.ownerLockable) {
// If our client grid is the normal side of a
// lockable grid, we listen to its lockable owner's
// beforereconfigure
reconfigurable = grid.ownerLockable;
reconfigurable.syncRowHeight = false;
lockedView = reconfigurable.lockedGrid.getView();

// Bind to locked view for key and mouse events
// Add row processor which adds collapsed class
lockedView.addRowTpl(me.addCollapsedCls).rowExpander = me;

// Refresh row heights of expended rows on the
// locked (non body containing) side upon lock &
// unlock.
// The locked side's expanded rows will collapse
// back because there's no body there
'columnschanged', me.refreshRowHeights, me);
'datachanged', me.refreshRowHeights, me);
me.beforeReconfigure, me);

if (grid.ownerLockable && !grid.rowLines) {
// grids without row lines can gain a border when
// focused. When they do, the
// stylesheet adjusts the padding of the cells so
// that the height of the row
// does not change. It is necessary to refresh the
// row heights for lockable
// grids on focus to keep the height of the expander
// cells in sync.
view.on('rowfocus', me.refreshRowHeights, me);

beforeReconfigure : function(grid, store, columns,
oldStore, oldColumns) {
var expander = this.getHeaderConfig();
expander.locked = true;

* @private Inject the expander column into the correct
* grid.
* If we are expanding the normal side of a lockable grid,
* poke the column into the locked side
addExpander : function() {
var me = this, expanderGrid = me.grid, expanderHeader = me

// If this is the normal side of a lockable grid, find
// the other side.
if (expanderGrid.ownerLockable) {
expanderGrid = expanderGrid.ownerLockable.lockedGrid;
expanderGrid.width += expanderHeader.width;
expanderGrid.headerCt.insert(0, expanderHeader);

getRowBodyFeatureData : function(record, idx, rowValues) {
var me = this;
me.self.prototype.setupRowData.apply(me, arguments);

rowValues.rowBody = me.getRowBodyContents(record);
rowValues.rowBodyCls = me.recordsExpanded[record.internalId] ? ''
: me.rowBodyHiddenCls;

setup : function(rows, rowValues) {
var me = this;
me.self.prototype.setup.apply(me, arguments);
// If we are lockable, the expander column is moved into
// the locked side, so we don't have to span it
if (!me.grid.ownerLockable) {
rowValues.rowBodyColspan -= 1;

bindView : function(view) {
if (this.expandOnEnter) {
view.on('itemkeydown', this.onKeyDown, this);
if (this.expandOnDblClick) {
view.on('itemdblclick', this.onDblClick, this);

onKeyDown : function(view, record, row, rowIdx, e) {
if (e.getKey() == e.ENTER) {
var ds = view.store, sels = view
.getSelectionModel().getSelection(), ln = sels.length, i = 0;

for (; i < ln; i++) {
rowIdx = ds.indexOf(sels[i]);
this.toggleRow(rowIdx, sels[i]);

onDblClick : function(view, record, row, rowIdx, e) {
this.toggleRow(rowIdx, record);

toggleRow : function(rowIdx, record) {
var me = this, view = me.view, rowNode = view
.getNode(rowIdx), row = Ext.fly(rowNode,
'_rowExpander'), nextBd = row.down(
me.rowBodyTrSelector, true), isCollapsed = row
.hasCls(me.rowCollapsedCls), addOrRemoveCls = isCollapsed ? 'removeCls'
: 'addCls', ownerLock, rowHeight, fireView;

// Suspend layouts because of possible TWO views having
// their height change
me.recordsExpanded[record.internalId] = isCollapsed;

// Sync the height and class of the row on the locked
// side
if (me.grid.ownerLockable) {
ownerLock = me.grid.ownerLockable;
fireView = ownerLock.getView();
view = ownerLock.lockedGrid.view;
rowHeight = row.getHeight();
row = Ext.fly(view.getNode(rowIdx), '_rowExpander');
} else {
fireView = view;
fireView.fireEvent(isCollapsed ? 'expandbody'
: 'collapsebody', row.dom, record, nextBd);
// Coalesce laying out due to view size changes

// refreshRowHeights often gets called in the middle of some
// complex processing.
// For example, it's called on the store's datachanged
// event, but it must execute
// *after* other objects interested in datachanged have done
// their job.
// Or it's called on column lock/unlock, but that could be
// just the start of a cross-container
// drag/drop of column headers which then moves the column
// into its final place.
// So this throws execution forwards until the idle event.
refreshRowHeights : function() {
idle : this.doRefreshRowHeights,
scope : this,
single : true

doRefreshRowHeights : function() {
var me = this, recordsExpanded = me.recordsExpanded, key, record, lockedView = me.grid.ownerLockable.lockedGrid.view, normalView = me.grid.ownerLockable.normalGrid.view, normalRow, lockedRow, lockedHeight, normalHeight;

for (key in recordsExpanded) {
if (recordsExpanded.hasOwnProperty(key)) {
record = this.view.store.data.get(key);
lockedRow = lockedView.getNode(record, false);
normalRow = normalView.getNode(record, false);
lockedRow.style.height = normalRow.style.height = '';
lockedHeight = lockedRow.offsetHeight;
normalHeight = normalRow.offsetHeight;
if (normalHeight > lockedHeight) {
lockedRow.style.height = normalHeight
+ 'px';
} else if (lockedHeight > normalHeight) {
normalRow.style.height = lockedHeight
+ 'px';

getHeaderConfig : function() {
var me = this;

return {
width : 24,
lockable : false,
sortable : false,
resizable : false,
draggable : false,
hideable : false,
menuDisabled : true,
tdCls : Ext.baseCSSPrefix + 'grid-cell-special',
innerCls : Ext.baseCSSPrefix
+ 'grid-cell-inner-row-expander',
renderer : function(value, metadata) {
// Only has to span 2 rows if it is not in a
// lockable grid.
if (!me.grid.ownerLockable) {
metadata.tdAttr += ' rowspan="2"';
return '<div class="' + Ext.baseCSSPrefix
+ 'grid-row-expander"></div>';
processEvent : function(type, view, cell, rowIndex,
cellIndex, e, record) {
if (type == "mousedown"
&& e.getTarget('.x-grid-row-expander')) {
me.toggleRow(rowIndex, record);
return me.selectRowOnExpand;

8 Oct 2013, 1:21 AM
Next time please at least put it into code brackets ....

Ok, but to get back to the problem, your Custom RowExpander also got those events, you can find the fire statement within the toggleRow function - which is executed on double click:

fireView.fireEvent(isCollapsed ? 'expandbody' : 'collapsebody', row.dom, record, nextBd); // Coalesce laying out due to view size changes

You said that it would not work, but you actually never tried?
Then i'd suggest you just try it out.
Any error output on the console? Or just nothing happening?

8 Oct 2013, 3:06 AM
Thanks for the reply, i have tried using expandbody but nothing happens, no error in the console. Even i put alert inside listeners, it is not going into listener if i use expand body. Here is the one which i had tried

listeners : {
expandbody: function(dv, record, item, index, e,
rowid) {
expandCaseRow(index, record);

8 Oct 2013, 4:00 AM
what is the output on the console if you do a
right before the event is triggered within the toggleRow function?

8 Oct 2013, 4:32 AM
Here is what i got in the console and database call did not happen inside expandRow() method
[Object] [Object]

8 Oct 2013, 8:29 AM
Ok, should have expressed more clearly what i wanted ;)
What are all properties/methods available for that object? I need the Details, not that it is an Object (because otherwise you'd have completely different problems ;) )

The reason i'm asking is simply the following:
To clarify where your event listeners need to go, i wanted to examine the view property on which they are fired, as defining listeners for that one will catch your events anyways.

8 Oct 2013, 11:46 PM
This is my complete code

plugins : [ {
ptype : 'customrowexpander',
pluginId : 'expander',
expandOnEnter : false,
rowBodyTpl : [
'<div style="overflow:auto;height: 773px;" class="details" id="details{someid}">',
'', '</div>' ]
} ],
listeners : {
itemdblclick : function(dv, record, item, index, e,
rowid) {
expandRow(index, record);
at the end i am calling function
function expandRow(index, record) {

url : 'showxpansion?someid='+someid,
scope : this,
success : function(response, opts) {
$('#details' + someid).html(response.responseText);
failure : function(response, opts) {
console.log('server-side failure with status code '
+ response.status);


In the above code snippet,the blocked code should not happen when i the row is already expanded

9 Oct 2013, 7:18 AM
Ok, either you try to get the listeners working - you should look up how to listen directly to the events fired by the view - , or you simply do it the dirty way and assign some variable to your record like myexpanded and set it true / false accordingly:

record.myexpanded = true;
expandRow(index, record);
else {

record.myexpanded = false;


Still it would be nice if you figured out how to do it via listeners. Your code at least theoretically seems to support this.

9 Oct 2013, 7:46 PM
Thank you. That works. Surely i will try with listeners if i get time.

17 Jul 2014, 3:45 PM

I am using sencha architect? Can anyone help me how to use row expander plugin?
I am not able to used it. I already adding as a resource and many others ways , but is not working.
Please help me in this.