PDA

View Full Version : Associated stores not buffered : issue applying groupHeaderTpl in grouping



ypandey
23 Jun 2013, 5:53 AM
Hi,

Issue: The rows and the children parameters of the groupHeaderTpl are undefined.

I have one main model(extended in a main store) which has 3 associated models(no hard-coded stores). Each associated children store(dynamic), is re-configured in a grid(so, 3 different grids). This being the scenario, I have a requirement of grouping the grid rows and show the count of the rows against each grouping. For this I had used the groupHeaderTpl. All the values come but the rows and the children attributes come as undefined.

In the 4.2.1 docs, it is documented that the row and children parameters are unavailable for buffered stores. Which means that these associated stores are buffered.

Is there a way to get around this?

Anand

DiscoBoy
9 Sep 2013, 11:36 PM
The problem I have is that rows/children are not even available when the store is not buffered...

jkyoutsey
18 Sep 2013, 7:57 AM
I have the same issue as DiscoBoy.

I have a non-buffered store and the children property is undefined. This results in a group header of something like:
"GroupHeaderName ("

And then the expand/collapse is broken.

The really weird thing is that it WORKS in a grid on one page, but is broken in the grid on another?! Doing the same basic thing the same basic way, and getting different results. The difference in the two is the models and stores. Niether store is buffered. Both models have some convert functions in them. In one everything works just fine. In the other, no matter what my groupingField is it fails trying to apply the {children.length} in the groupHeaderTpl.

Now, I could just opt for not showing the count for the group, but then I have inconsistent results from one grid to the next. Not a good thing.

I cannot reproduce this outside of my environment in any simple manner. It always seems to work. So that means that is something odd about:
1. The data?
2. The model
3. The store

Here is both good and bad model/store/view snippets: (names have been changed to protect the innocent!)

/*
* BAD MODEL
*/
Ext.define('MY.model.BadModel', {
extend : 'Ext.data.Model',
idProperty : 'badId',
fields : ['badId', 'name', 'parentId', 'parentName', 'childId', 'childName', 'leafId', 'leafName',
{
name : 'startTime',
type : 'date'
}, {
name : 'endTime',
type : 'date'
}, 'reason', 'sponsor', {
name : 'force',
type : 'boolean'
}, {
name : 'expired',
type : 'boolean',
convert : function(value, record) {
if (typeof record !== 'undefined') {
var now = new Date();
var endTime = record.get('endTime');
if (!endTime) {
return false;
}
return now >= endTime;
}
}
}, {
name : 'createdBy',
type : 'string',
defaultValue : 'X'
}, {
name : 'parentAndChild',
type : 'string',
convert : function(value, record) {
if (typeof record !== 'undefined') {
return record.get('parentName') + ' : ' + record.get('childName');
}
}
}]
});

/*
* BAD STORE
*/
Ext.define('MY.store.badsStore', {
extend : 'Ext.data.Store',
model : 'MY.model.BadModel',
groupField : 'parentAndChild',
sorters : [{
property : 'parentName',
direction : 'ASC'
}, {
property : 'childName',
direction : 'ASC'
}, {
property : 'leafName',
direction : 'ASC'
}, {
property : 'name',
direction : 'ASC'
}],
proxy : {
type : 'ajax',
url : '/GetBadData.json',
reader : {
type : 'json',
root : 'results'
}
}
});

/*
* BAD GRID (only initComponent shown)
*/
initComponent : function() {
this.store = Ext.data.StoreManager.lookup('BadStore');
var loc = l10n.badView;

// Formatters for group headers
Ext.util.Format.dateFormat = function(val) {
if (val) {
return Ext.util.Format.date(val, l10n.dateTimeFormatStrings.shortDateFormat + " "
+ l10n.dateTimeFormatStrings.longTimeFormat);
} else {
return loc.noExpirationDateGroupHeaderText;
}
};

// Conditionally apply the formatter(s) to the group headers.
var template = new Ext.XTemplate('<tpl if="this.isDate(groupField)">', '{name:dateFormat} ({children.length}}',
'<tpl else>', '{name} ({children.length})', '</tpl>', {
isDate : function(groupField) {
return groupField === 'startTime' || groupField === 'endTime';
},
compiled : true
});

this.features = Ext.create('Ext.grid.feature.Grouping', {
groupHeaderTpl : template,
hideGroupedHeader : true,
startCollapsed : true
});

this.columns = [{
header : loc.parentAndChildNameColHeader,
dataIndex : 'parentAndChild',
flex : 3
}, {
header : loc.leafNameColHeader,
dataIndex : 'leafName',
flex : 3
}, {
header : loc.nameColHeader,
dataIndex : 'name',
flex : 3
}, {
header : loc.sponsorColHeader,
dataIndex : 'sponsor',
flex : 1
}, {
header : loc.startTimeColHeader,
dataIndex : 'startTime',
renderer : this.dateColumnRenderer,
flex : 1
}, {
header : loc.endTimeColHeader,
dataIndex : 'endTime',
renderer : this.dateColumnRenderer,
flex : 1

}];

this.callParent(arguments);
},

/*
* GOOD MODEL
*/
Ext.define('MY.model.GoodModel', {
extend : 'Ext.data.Model',
idProperty : 'generatedPrimaryKey',
fields : [{
name : 'parentId',
type : 'string'
}, {
name : 'parentName',
type : 'string'
}, {
name : 'active',
type : 'boolean',
defaultValue : true
}, {
name : 'childName',
type : 'string'
}, {
name : 'editable',
type : 'boolean',
defaultValue : true
}, {
name : 'assigned',
type : 'boolean',
defaultValue : false
}, {
name : 'updatedBy',
type : 'string'
}, {
name : 'theDate',
type : 'date',
convert : function(value, record) {
// This is (somehow) a local date.
if (value) {
var date = new Date(value);
return date;
} else {
return null;
}
}
}, {
name : 'lastUpdateDate',
type : 'date',
convert : function(value, record) {
// This is (somehow) a local date.
if (value) {
var date = new Date(value);
return date;
} else {
return null;
}
}
}, {
name : 'childId',
type : 'string'
}, {
name : 'isUserCreated',
type : 'boolean',
mapping : 'userCreated',
defaultValue : true
}, {
name : 'generatedPrimaryKey',
type : 'string',
convert : function(value, record) {
if (typeof record !== 'undefined') {
return record.get('parentId') + record.get('childId');
}
}
}]
});

/*
* GOOD STORE
*/
Ext.define('MY.store.GoodStore', {
extend : 'Ext.data.Store',
model : 'MY.model.GoodModel',
groupField : 'parentName',
sorters : [{
property : 'parentName',
direction : 'ASC'
}, {
property : 'childName',
direction : 'ASC'
}],
proxy : {
type : 'ajax',
url : '/GetGoodData.json',
reader : {
type : 'json',
root : 'results'
}
}
});

/*
* GOOD VIEW
*/
initComponent : function() {
this.store = Ext.data.StoreManager.lookup('GoodStore');
var loc = l10n.goodView;

// Formatters for group headers
Ext.util.Format.dateFormat = this.dateColumnFormat;
Ext.util.Format.editableIconFormat = this.editableIconFormat;
Ext.util.Format.assignedIconFormat = this.assignedIconFormat;
Ext.util.Format.activeIconFormat = this.activeIconFormat;

// Conditionally apply the formatter(s) to the group headers.
var count = ' ({children.length})';
var nameCount = ': {name}' + count;
var template = new Ext.XTemplate('<tpl if="this.isDate(groupField)">', loc.dateColHeader,
': {name:dateFormat}', count, '<tpl elseif="this.isEditableField(groupField)">', loc.editableColHeader,
': {name:editableIconFormat}', count, '<tpl elseif="this.isActiveField(groupField)">', loc.activeColHeader,
': {name:activeIconFormat}', count, '<tpl elseif="this.isAssignedField(groupField)">',
loc.assignedColHeader, ': {name:assignedIconFormat}', count, '<tpl elseif="this.isParentField(groupField)">',
loc.titleColHeader, nameCount, '<tpl elseif="this.isChildField(groupField)">', loc.childColHeader, nameCount,
'<tpl else>', '{name}', count, '</tpl>', {
isDate : function(groupField) {
return groupField == 'theDate';
},
isEditableField : function(groupField) {
return groupField == 'editable';
},
isActiveField : function(groupField) {
return groupField == 'active';
},
isAssignedField : function(groupField) {
return groupField == 'assigned';
},
isParentField : function(groupField) {
return groupField == 'parentName';
},
isChildField : function(groupField) {
return groupField == 'childName';
},
compiled : true
});

this.features = Ext.create('Ext.grid.feature.Grouping', {
groupHeaderTpl : template,
hideGroupedHeader : true,
startCollapsed : true
});

this.columns = [{
header : loc.parentColHeader,
dataIndex : 'parentName',
flex : 6
}, {
header : loc.childColHeader,
dataIndex : 'childName',
flex : 6
}, {
header : loc.editableColHeader,
dataIndex : 'editable',
flex : 1,
renderer : this.editableColumnRenderer
}, {
header : loc.activeColHeader,
dataIndex : 'active',
flex : 1,
renderer : this.activeColumnRenderer
}, {
header : loc.assignedColHeader,
dataIndex : 'assigned',
flex : 1,
renderer : this.assignedColumnRenderer
}, {
header : loc.publishedDateColHeader,
dataIndex : 'publishedDate',
flex : 2,
renderer : this.dateColumnRenderer
}];

this.callParent(arguments);
},

DiscoBoy
18 Sep 2013, 8:00 AM
My temporary fix for this issue ...



// TODO: ExtJS 4.2.1 BUG -> Children wouldnt be available otherwise in "groupHeaderTpl" !!!
if(Ext.getVersion('core').isGreaterThan('4.2.0') && this.displayType == 'grouped') {
Ext.Function.interceptAfter(this.view.getFeature('xmppRosterGrouping'), 'setupRowData', function(record, idx, rowValues) {
this.groupInfo.children = this.groupInfo.rows = this.grid.getStore().groups.getByKey(this.groupInfo.name).records;
});
}

jkyoutsey
18 Sep 2013, 8:10 AM
DiscoBoy, where are you putting this workaround? In the controller? Before loading the store?

DiscoBoy
18 Sep 2013, 8:19 AM
Simply in the initComponent method of the grid...

1) ... callParent()
2) ...now create the interceptor

@Sencha: Please fix this bug!

jkyoutsey
18 Sep 2013, 8:38 AM
DiscoBoy, That ALMOST works!
The header display is right now. It shows the count.
But "startCollapsed:true" is ignored and nothing happens when clicking the header.
No errors on load, but when trying to collapse the groups I get this stack:

Uncaught TypeError: Cannot read property 'length' of undefined ext-all-dev.js:157882
Ext.define.collapseGroup ext-all-dev.js:157882
Ext.define.doCollapseExpand ext-all-dev.js:158908
Ext.define.collapse ext-all-dev.js:158808
Ext.define.onGroupClick ext-all-dev.js:159021
fire ext-all-dev.js:16019
continueFireEvent ext-all-dev.js:17883
fireEventArgs ext-all-dev.js:17854
prototype.fireEventArgs ext-all-dev.js:60492
fireEvent ext-all-dev.js:17831
Ext.define.processSpecialEvent ext-all-dev.js:151214
Ext.define.processItemEvent ext-all-dev.js:151151
Ext.define.processUIEvent ext-all-dev.js:137105
Ext.define.handleEvent ext-all-dev.js:137044
(anonymous function)
Ext.apply.createListenerWrap.wrap ext-all-dev.js:16924

And I totally agree. Sencha is not doing sufficient up front testing on the grid to ensure that it is stable. Grouping, formatting, editing. The grid is so inconsistent. Sencha needs to fix this.

jkyoutsey
18 Sep 2013, 9:07 AM
So, I have to say, this is not straightforward. I believe it has something to do with HOW the STORE is bound to the grid. I found a "fix" another dev put in for a 4.2.0 issue, remove it and DiscoBoy's fix and everything works great!