How to merge cells in an extjs grid panel? - extjs

I have extjs grid.Panel and I am trying to mimic cell merging in the rows when there is duplicate data in the first column of consecutive rows. Using a column renderer, I clear a row's first column value if it is a duplicate of a previous row's value. I would like to modify the previous row's first cell's tdAttr to row span the appropriate number of rows. I cannot figure out how to get to a previous row's metaData. Below is my current renderer code.
renderer: function(value, metaData, record, row, col, store, gridView){
var dataIndex = metaData.column.dataIndex;
var overrideValue = value;
// check previous rows and determine whether this row's first column has the same
// value as a previous row's first column
if (col == 0){
var i = row-1;
while (i >= 0 && overrideValue != ""){
if (store.data.items[i].get(dataIndex) == value){
overrideValue = "";
// ----- here i want to update record i's metaData...
// metaData.tdAttr = 'rowspan=' + row-i+1
}
i--;
}
}
return overrideValue;
}
I've tried a number of ways to get at the metaData associated with a record but have been unsuccessful. The getnodes and getnode methods of the gridView object return undefined.

I created the following solution. I used a combination of a column renderer and a view listener, specifically "viewready". The grid's row line is set to "false" and I use the column renderer to apply a custom style to replace the row line as appropriate and clear the appropriate cells values. I modify the background color of "merged" cells in the "viewready" listener.
<style>
.mywrapcell .x-grid-cell-inner {
white-space: normal;
border-top-color: #c6c6c6;
border-top-width: 1px;
border-top-style: solid;
}
</style>
_addGrid: function() {
var myModel = Ext.define('User', {
extend: 'Ext.data.Model',
fields: [
{name: 'col1', type: 'string'},
{name: 'col2', type: 'string'},
]
});
var myData = [{col1: 'U2', col2: 'Jan 5'},
{col1: 'U2', col2: 'Jan 6'},
{col1: 'Crowded House', col2: 'Feb 19'},
{col1: 'Jack Johnson', col2: 'Apr 4'},
{col1: 'Jack Johnson', col2: 'Apr 5'},
{col1: 'Cage the Elephant', col2: 'May 10'},
{col1: 'Cage the Elephant', col2: 'May 11'},
{col1: 'Blondie', col2: 'Jun 6'},
{col1: 'Gorillaz', col2: 'Jun 19'},
{col1: 'Gorillaz', col2: 'Jun 20'}];
var mystore = Ext.create('Ext.data.Store', {
model: myModel,
data: myData,
proxy: {
type: 'memory',
enablePaging: false
}
});
var mygrid = Ext.create('Ext.grid.Panel', {
store: mystore,
itemId: 'grid',
viewConfig:{
listeners: {
viewready: function(view){
var dataIndex = 'col1';
// find the index of the column for row spanning
var colindex = view.ownerCt.columns.findIndex((x) => x.dataIndex == dataIndex);
if (colindex != -1){
var viewrows = view.getNodes();
var bgcolor = '#fff';
// set all the cells to the
// same background color
for(i=0;i<viewrows.length;i++){
viewrows[i].cells[colindex].style.backgroundColor = bgcolor;
}
}
}
},
enableEditing: false,
enableBulkEdit: false,
showRowActionsColumn: false,
enableRanking: false,
sortableColumns: false,
columnLines: true,
rowLines: false, //row lines handled in renderer
width: 225,
columns: [
{text: 'Artist',
dataIndex: 'col1',
flex: 1,
renderer: function(value, metaData, record, row, col, store, view){
var overrideValue = value;
// check previous rows and determine whether this row's first column has the same
// value as a previous row's first column
var i = row-1;
while (i >= 0 && overrideValue != ""){
if (store.data.items[i].get(metaData.column.dataIndex) == value){
overrideValue = '';
}
i--;
}
if (overrideValue != ""){
metaData.tdCls = 'mywrapcell';
}
return overrideValue;
}
},
{
text: 'Show Date',
dataIndex: 'col2',
tdCls: 'mywrapcell'
}
],
});
this.add(mygrid);
}
and the result...

Related

Whats config like renderer in extjs picker?

I'm developing a web application using Extjs-6. I want to extend a class from Ext.form.field.Picker. I do it as follow:
...
extend: 'Ext.form.field.Picker',
createPicker: function(){
return new Ext.panel.Panel({
items: [{
xtype: 'textfield',
name: 'text',
fielLabel: 'text label'
}, {
xtype: 'colorfield',
name: 'color',
fielLabel: 'color field'
},
...
]
});
}
...
my value in this class is an object as follow:
{
text: 'value of textfield',
color: 'value of colorfield'
}
but when I set this object to value of class it shown in picker as [object object].
How Can I d?
Have the picker a confis like renderer to get the value of picker and then return correct string?
There is more to it than just template.
Below is example picker implementation for textfield + datefield, just adjust it to have colorfield instead.
// component has picker with both textfield and datefield;
// when picker is collapsed, data is displayed as "{text}, {date}"
Ext.define('ColorPicker', {
extend: 'Ext.form.field.Picker',
// picker template
config: {
popup: {
lazy: true,
$value: {
xtype: 'window',
closeAction: 'hide',
referenceHolder: true,
minWidth: 540,
minHeight: 60,
layout: 'form',
header: false,
resizable: true,
items: [
{
xtype: 'textfield',
name: 'text',
fielLabel: 'text label',
anchor: '100%',
reference: 'text'
},
{
xtype: 'datefield',
name: 'color',
fielLabel: 'color field',
anchor: '100%',
format: 'd.m.Y',
reference: 'date'
}
],
fbar: [
{ text: 'OK', reference: 'okBtn' },
{ text: 'Cancel', reference: 'cancelBtn' }
]
}
}
},
dateFormat: 'd.m.Y',
createPicker: function(){
var me = this,
popup = me.getPopup();
// the window will actually be shown and will house the picker
me.pickerWindow = popup = Ext.create(popup);
popup.lookupReference('okBtn').on('click', 'onPickerOk', me);
popup.lookupReference('cancelBtn').on('click', 'onPickerCancel', me);
popup.on({
close: 'onPickerCancel',
scope: me
});
me.updateValue(me.getValue());
return popup;
},
// ok picker button handler
onPickerOk: function () {
var me = this,
popup = me.pickerWindow,
textField = popup.lookupReference('text'),
dateField = popup.lookupReference('date'),
value = {
text: textField.getValue(),
date: dateField.getValue()
};
popup.hide();
me.setValue(value);
},
// cancel picker button handler
onPickerCancel: function () {
var me = this,
popup = me.pickerWindow;
popup.hide();
me.updateValue(me.getValue());
},
// override set value to support both string ("{text}, {date}")
// and object ({ text: "{text}", date: "{date}" })
setValue: function(value) {
var me = this,
text,
date,
v;
if (Ext.isObject(value)) {
value = value.text + ", " + Ext.Date.format(value.date, me.dateFormat);
}
me.callParent([ value ]);
// always update in case opacity changes, even if value doesn't have it
// to handle "hex6" non-opacity type of format
me.updateValue(value);
},
// update values in picker fields
updateValue: function (value) {
var me = this,
popup = me.pickerWindow,
textField,
dateField,
text = value.text,
date = value.date;
if (!popup || !popup.isComponent) {
return;
}
if (Ext.isString(value)) {
value = value.split(',');
text = (value[0] || '').trim();
date = Ext.Date.parse((value[1] || '').trim(), me.dateFormat);
} else if (Ext.isObject(value)) {
text = value.text || '';
date = value.date || '';
}
textField = popup.lookupReference('text');
dateField = popup.lookupReference('date');
if (!me.syncing) {
me.syncing = true;
textField.setValue(text);
dateField.setValue(date);
me.syncing = false;
}
}
});
Fiddle: https://fiddle.sencha.com/#fiddle/14kg

Angularjs ui grid with grouping column sequence is getting jumbled up

I am using AngularJs ui grid with grouping. The table is displaying fine but the problem I am facing is that the months sequence in the table is getting jumbled up. Please find my Plunker. In the table the Months are appearing in the jumbled up sequence 11-14, 05-15, 04-15, ... 02-15. But I need it in the sequence of 11-14, 12-14, 01-15, 02-15, 03-15, 04-15, 05-15. Can any one help me to fix it?
I am using the following for to get colDefs:
$scope.getColumnDefsForUiGrid = function(columns) {
var colDef = [];
colDef.push({
name: 'mode',
grouping: {
groupPriority: 0
},
displayName: 'Type',
enableCellEdit: false,
width: '5%'
});
colDef.push({
name: 'service',
displayName: 'Catg',
enableCellEdit: false,
width: '5%'
});
angular.forEach(columns, function(value, key) {
colDef.push({
name: key,
displayName: value,
enableCellEdit: true,
aggregationType: uiGridConstants.aggregationTypes.sum,
width: '5%'
})
});
return colDef;
};
Here is a workaround for your problem
you can see it working on this plunker
$scope.getColumnDefsForUiGrid = function( columns ){
var colDef = [];
colDef.push({name: 'mode', grouping: { groupPriority: 0 }, displayName: 'Type', enableCellEdit: false, width: '5%'});
colDef.push({name: 'service', displayName: 'Catg', enableCellEdit: false, width: '5%'});
//I split the monthColumns into an other array
var monthCol = [];
angular.forEach(columns, function( value, key ) {
monthCol.push({name: key, displayName: value, enableCellEdit: true, aggregationType : uiGridConstants.aggregationTypes.sum, width: '5%' })
});
//I sort this array using a custom function
monthCol.sort(function(a,b){
a = a.displayName.split("-");
b = b.displayName.split("-");
if(a[1] < b[1]){
return -1;
}else if (a[1] > b[1]){
return 1;
}else {
if(a[0] < b[0]){
return -1;
}else{
return 1;
}
}
});
//I concat the two array
return colDef.concat(monthCol);
};

How to find grid array in the browser ExtJs

Suppose if i have created a grid store like this
var store = Ext.create('Ext.data.ArrayStore', {
fields:['id','title', 'director', 'released', 'genre','tagline', 'price', 'available'],
data: [
[
1,
"Office Space",
"Mike Judge",
"1999-02-19",
1,
"Work Sucks",
"19.95",
1
],
[
3,
"Super Troopers",
"Jay Chandrasekhar",
"2002-02-15",
1,
"Altered State Police",
"14.95",
1
]
]
});
when i run it on browser i could not see anything because it has already been saved in the browser's memory , we need to display it to the the grid to see those data.
if i am editing the grid in the browser using editors plugin, so how can i see the changes made to the grid store? how to see it ?
You can add storeId to the store and then you can use the following global function:
Ext.StoreManager.lookup('storeId');
with this function you can always get the store from anywhere.
the gridpanel has the edit( editor, e, eOpts ) event which can be used after editing is complete.
Example:
var store = Ext.create('Ext.data.ArrayStore', {
storeId: 'gridStore',
(...)
});
var grid = Ext.create('Ext.grid.Panel', {
store: store,
(...),
listeners: {
edit: function(editing, e, eOpts) {
var record = e.record;
console.log('Changes on this record: ', e.record.getChanges());
console.log('Original value: ', (editing.ptype == 'cellediting' ? e.originalValue : e.originalValues));
console.log('New value: ', (editing.ptype == 'cellediting' ? e.value : e.newValues));
}
}
});
//for save on a toolbar
var button = Ext.create('Ext.button.Button', {
text: 'Save',
(...),
handler: function() {
var gridStore = Ext.StoreManager.lookup('gridStore');
console.log('all added, but not synchronized records: ', gridStore.getNewRecords());
console.log('all edited, but not synchronized records: ', gridStore.getUpdatedRecords());
console.log('all modified(added and edited), but not synchronized records:', gridStore.getModifiedRecords());
console.log('all removed, but not synchronized records:', gridStore.getRemovedRecords());
}
});
I'd say it depend.
First, to display you store in a grid, it should be something like this (simplified), following your code:
var grid = Ext.create('Ext.grid.Panel', {
title: 'Test',
store: store,
columns: [
{ text: 'title', dataIndex: 'title' },
{ text: 'director', dataIndex: 'director', flex: 1 }
],
height: 200,
width: 400
});
var MyWindow = Ext.create('widget.window',{width:400,height:200,items:[grid]});
MyWindow.show();
You assigned your store to a local variable "store". Normaly, if you use that store in a grid, and you make changes in that grid, it should reflect in the store.
When you make it editable with the editable grid plugin, changes are directly writen in the store, so this should work:
var currentStoreContent = store.data.items;
or, from the grid:
var currentStoreContent = grid.getStore().data.items

extjs 4.1.0 Checkcolumn with grouping plagin - how to make all checkboxes in a group (not grouped checkboxes) to be checked or unchecked together?

I have a grid and use rowactions grouping plugin to have an action button in the grouping header. The fist visible column in the grid is a checkbox column. I need to make the checkboxes in the same group behave the same way. 1) if a user checks one of the checkboxes, all boxes in the same group has to be checked. 2) Also I need to look at another field in the same row and if it doesn't have a value, disable checkboxes (all in the same group), otherwise all checkboxes should be enabled.
I also tried xtype:checkbox. For #1 I just cannot figure out how to get the siblings. I tried using the checkchange - I could get a record there - but I don't know how to get other records from the same group. For #2 - how do I get the record - I need to check one of the values and set the ckeckbox enabled/disabled.
Any ideas?
Thank you.
EDIT: for #2 I went with disable checkcolumn for some rows answer here
This is my index.cshtml
<script type="text/javascript">
Ext.require([
'Ext.container.Viewport',
'Ext.grid.*',
'Ext.util.*',
'Ext.Date.*',
'Ext.ux.CheckColumn',
'ACTGRID.ui.GroupPanelGrid'
]);
Ext.onReady(function () {
initialize();
});
function initialize() {
Ext.Ajax.timeout = 60000; // 60 seconds
var myGrid = Ext.create('ACTGRID.ui.GroupPanelGrid');
var viewport = Ext.create('Ext.container.Viewport', {
layout: 'border',
items: [{
region: 'center',
title: 'Grid',
items: myGrid }]
});
};
</script>
This is the GroupPanelGrid.js:
Ext.Date.patterns = {
ISO8601Long: "Y-m-d H:i:s",
ISO8601Short: "Y-m-d",
ShortDate: "n/j/Y",
LongDate: "l, F d, Y",
FullDateTime: "l, F d, Y g:i:s A",
MonthDay: "F d",
ShortTime: "g:i A",
LongTime: "g:i:s A",
SortableDateTime: "Y-m-d\\TH:i:s",
UniversalSortableDateTime: "Y-m-d H:i:sO",
YearMonth: "F, Y"
};
Ext.define('ACTGRID.ui.TransactionsLateModel', {
extend: 'Ext.data.Model',
fields: [
{ name: 'Id', type: 'int' },
{ name: 'Approval', type: 'boolean' },
{ name: 'ErrorComment', type: 'string' },
{ name: 'ErrorSource', type: 'string'},
{ name: 'RecordDate', type: 'date', dateFormat: 'MS' }],
idProperty: 'Id'
});
Ext.define("ACTGRID.ui.GroupPanelGrid", {
extend: "Ext.grid.Panel",
requires: ['ACTGRID.ui.TransactionsLateModel',
'Ext.ux.grid.RowActions',
'Ext.grid.feature.Grouping'],
initComponent: function () {
var me = this;
me.features = [Ext.create('Ext.grid.feature.Grouping', {
groupHeaderTpl: 'Grouping Header: {name} ({rows.length}
Item{[values.rows.length > 1 ? "s" : ""]})',
enableGroupingMenu: false })];
me.columns = me.buildColumns();
me.store = Ext.create('Ext.data.Store', {
model: 'ACTGRID.ui.TransactionsLateModel',
remoteSort: true,
storeId: 'TransactionsLateStoreId',
autoLoad: true,
buffered: true,
autoSync: true,
groupField: 'RecordDate',
pageSize: 70,
proxy: {
type: 'rest',
timeout: 600000,
url: '/Home/ActionsGrid/',
reader: {
type: 'json',
root: 'transaction',
totalProperty: "Total"
},
writer: {
type: 'json',
root: 'transaction'
}
}
});
me.autoSizeColumns = true;
me.autoScroll = true;
me.forcefit = true;
me.callParent(arguments);
},
buildColumns: function () {
var me = this;
return [
{
xtype: 'rowactions', hidden: true, hideable: false,
actions: [{}],
keepSelection: true,
groupActions: [{
iconCls: 'icon-grid',
qtip: 'Action on Group',
align: 'left',
callback: function (grid, records, action, groupValue) {
var rec = [].concat(records);
/*does something unrelated*/
}
}]
},
{
text: 'Approval', dataIndex: 'Approval', sortable: true,
width: 50,
xtype: 'checkcolumn',
listeners: {
checkchange: function (column, recordIndex, checked) {
var record = me.getStore().data.items[recordIndex].data;
/* Do something here? - How do I get all the siblings */
},
beforerender: function (e, eOpts) {
/* How do I check another cell in this row to see if it has a value
And disable the ckeckbox and set it read only? */
}
}
},
{ text:'ErrorComment', dataIndex: 'ErrorComment' },
{ text:'ErrorSource', dataIndex: 'ErrorSource'},
{ text:'RecordDate', dataIndex: 'RecordDate', renderer: Ext.util.Format.dateRenderer (Ext.Date.patterns.ShortDate)}];
},
height: 600,
width: 'auto'
});
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
[AcceptVerbs(HttpVerbs.Get)]
[ActionName("ActionsGrid")]
public ActionResult GetActionsGrid()
{
DetailsViewModel model = new DetailsViewModel();
List<ExtJsTreeGrid.Models.ActionGrid> ActionsFromDB = model.GetActionsGrid();
model.transaction = ActionsFromDB;
model.success = true;
return Json(model, JsonRequestBehavior.AllowGet);
}
[AcceptVerbs(HttpVerbs.Put)]
[ActionName("ActionsGrid")]
public ActionResult SetActionsGrid()
{
// do something
}
}
Model:
public class ActionGrid
{
public Int32 Id { get; set; }
public bool Approval { get; set; }
public string ErrorComment { get; set; }
public string ErrorSource { get; set; }
public DateTime RecordDate { get; set; }
}
public class DetailsViewModel
{
public List<ActionGrid> transaction = new List<ActionGrid>();
public bool success = true;
public List<ActionGrid> GetActionsGrid()
{
return new List<ActionGrid> {
new ActionGrid { Id = 1,
Approval = true,
ErrorComment = "Comment",
ErrorSource = string.Empty,
RecordDate = new DateTime(1979, 11, 23)
},
new ActionGrid { Id = 5,
Approval = true,
ErrorComment = "Comment",
ErrorSource = "brodas",
RecordDate = new DateTime(1979, 11, 23)
},
new ActionGrid { Id = 6,
Approval = true,
ErrorComment = "Comment",
ErrorSource = string.Empty,
RecordDate = new DateTime(1979, 11, 23)
},
new ActionGrid { Id = 7,
Approval = true,
ErrorComment = string.Empty,
ErrorSource = "brodas",
RecordDate = new DateTime(1979, 11, 23)
},
new ActionGrid { Id = 2,
Approval = true,
ErrorComment = string.Empty,
ErrorSource = string.Empty,
RecordDate = new DateTime(1980, 05, 20)
},
new ActionGrid { Id = 3,
Approval = true,
ErrorComment = "Comment",
ErrorSource = "brodas",
RecordDate = new DateTime(1995, 01, 12)
},
new ActionGrid { Id = 4,
Approval = true,
ErrorComment = string.Empty,
ErrorSource = string.Empty,
RecordDate = new DateTime(2010, 09, 02)
},
new ActionGrid { Id = 8,
Approval = true,
ErrorComment = string.Empty,
ErrorSource = "brodas",
RecordDate = new DateTime(2010, 09, 02)
},
new ActionGrid { Id = 9,
Approval = true,
ErrorComment = "Comment Errors",
ErrorSource = string.Empty,
RecordDate = new DateTime(2010, 09, 02)
},
new ActionGrid { Id = 10,
Approval = true,
ErrorComment = "Comment Errors",
ErrorSource = "brodas",
RecordDate = new DateTime(2010, 09, 02)
},
new ActionGrid { Id = 11,
Approval = true,
ErrorComment = string.Empty,
ErrorSource = "brodas",
RecordDate = new DateTime(2010, 09, 02)
}};
}
For requirement #1, the checkchange listener is indeed the right place to implement it.
To get all the records in the group, you need to grab a reference to the grid's store, and then use its query method, with your grouping field, and the value in the checked record.
Here's an example:
checkchange: function(col, index, checked) {
var grid = col.up('tablepanel'),
store = grid.getStore(),
record = store.getAt(index),
group = record.get('group'),
// returns a MixedCollection (not a simple array)
groupRecords = store.query('group', group);
// apply the same check status to all the group
groupRecords.each(function(record) {
record.set('checked', checked);
});
}
For your second requirement, I don't really understand when you want it to happen.
Anyway, the check column doesn't support disabling at the row level, so you'll have to implement that first.
The simplest way to do that is to add a field to your model to hold the disabled state. Let's call it 'disabled'. Then, you need two things.
First, render the checkbox disabled state. For that, let's override the renderer of the check column:
,renderer: function(value, meta) {
// Adding disabled class according to record's checkable
// field
if (meta.record.get('disabled')) {
meta.tdCls += ' ' + this.disabledCls;
}
// calling the overridden method (cannot use callParent
// since we haven't define a new class)
return Ext.grid.column.CheckColumn.prototype.renderer.apply(this, arguments);
}
Next, we need to prevent checking or unchecking disabled records. That can be done in the beforecheckchange event:
,beforecheckchange: function(col, index) {
var grid = col.up('tablepanel'),
store = grid.getStore(),
record = store.getAt(index);
if (record.get('disabled')) {
return false;
}
}
Now, you can just enable/disable a record's checkbox by setting the record's 'disabled' field.
record.set('disabled', true); // enable
record.set('disabled', false); // disable
Complete Example
Here's a complete example that implements your first requirement and, maybe, your second one. If that not what you meant for the second requirement, that should take you almost there...
The example can be seen running in this fiddle.
Ext.widget('grid', {
renderTo: Ext.getBody()
,height: 300
,features: [{ftype: 'grouping'}]
,store: {
proxy: {type: 'memory', reader: 'array'}
,fields: [
'name', // something to see
'group', // grouping field
'checkable', // for req #2
'checked', // for check column
'disabled' // for disabled state
]
,groupField: 'group'
,data: [
['Foo 1', 'foo', true],
['Foo 2', 'foo', false],
['Foo 3', 'foo', true],
['Bar 1', 'bar', false],
['Bar 2', 'bar', true],
['Baz 1', 'baz', false],
['Baz 2', 'baz', false],
['Baz 3', 'baz', true]
]
}
,columns: [{
xtype: 'checkcolumn'
,width: 50
,dataIndex: 'checked'
,listeners: {
checkchange: function(col, index, checked) {
var grid = col.up('tablepanel'),
store = grid.getStore(),
record = store.getAt(index),
group = record.get('group'),
// returns a MixedCollection (not a simple array)
groupRecords = store.query('group', group),
// for req #2
disableGroup = !record.get('checkable');
groupRecords.each(function(record) {
record.set('checked', checked);
// Here's how to disable the group... If that's
// really what you want.
record.set('disabled', disableGroup);
});
}
// Returning false from this listener will prevent the
// check change. This is used to implement disabled
// checkboxes.
,beforecheckchange: function(col, index) {
var grid = col.up('tablepanel'),
store = grid.getStore(),
record = store.getAt(index);
if (record.get('disabled')) {
return false;
}
}
}
,renderer: function(value, meta) {
// Adding disabled class according to record's checkable
// field
if (meta.record.get('disabled')) {
meta.tdCls += ' ' + this.disabledCls;
}
// calling the overridden method (cannot use callParent
// since we haven't define a new class)
return Ext.grid.column.CheckColumn.prototype.renderer.apply(this, arguments);
}
},{
dataIndex: 'name'
,text: "Name"
,flex: 1
},{
dataIndex: 'checkable'
,text: "Checkable"
}]
});
as for my case, i have a separate checkbox outside my gridpanel which controls the selection of the checkcolumn.
//Avoid view update after each row
grid.store.suspendEvents();
grid.store.each(function(rec) {
rec.set('ctrl_select_flag', ctrl.value);
});
grid.store.resumeEvents();
grid.getView().refresh();
ctrl.value refers to the component which controls my checkcolumn selection. ctrl_select_flag refers to the dataIndex of my checkcolumn.

ExtJs -> How to create grouped bar chart using 2 related Models (hasmany relationship)

I am trying to plot a grouped bar chart in ExtJs similar to Sencha's example.
Can you use a field that is a field of the Child Model belonging to the chart's store's Model in the Series xField / yField?
If a "Golfer" Model has many "GolfClubs", is it possible to render a grouped bar chart showing bars for each GolfClub that belongs to a Golfer (each golfer's name will be an axis label)?
In sencha's example the store has all the data in the one record but I'm hoping it can bind automatically to a "hasmany" associated Model?
//Models
Ext.define('GolfClub', {
extend : 'Ext.data.Model',
fields : [{
name : 'ClubType',
type : 'string'
}, {
name : 'Weight',
type : 'float'
}]
});
Ext.define('Golfer', {
extend : 'Ext.data.Model',
requires: ['GolfClub'],
fields : [{
name : 'GolferName',
type : 'string'
}],
hasMany: {model: 'GolfClub', name: 'golfClubs'}
});
//end Models
//Local Data (just to get it working first)
function data(){
var golfers = [];
var rory = Ext.create('Golfer', {
GolferName : 'Rory'
});
var rorysDriver = Ext.create('GolfClub', {
ClubType : 'Driver',
Weight : 80
});
var rorysPutter = Ext.create('GolfClub', {
ClubType : 'Putter',
Weight : 60
});
var rorysSandWedge = Ext.create('GolfClub', {
ClubType : 'SandWedge',
Weight : 50
});
var rorysClubs = rory.golfClubs();
rorysClubs.add(rorysDriver);
rorysClubs.add(rorysPutter);
rorysClubs.add(rorysSandWedge);
golfers.push(rory);
var tiger = Ext.create('Golfer', {
GolferName : 'Tiger'
});
var tigersDriver = Ext.create('GolfClub', {
ClubType : 'Driver',
Weight : 85
});
var tigersPutter = Ext.create('GolfClub', {
ClubType : 'Putter',
Weight : 55
});
var tigersSandWedge = Ext.create('GolfClub', {
ClubType : 'SandWedge',
Weight : 58
});
var tigersClubs = tiger.golfClubs();
tigersClubs.add(tigersDriver);
tigersClubs.add(tigersPutter);
tigersClubs.add(tigersSandWedge);
golfers.push(tiger);
return golfers;
}
//end Local Data
//Local Store
function store1(){
var golferStore = Ext.create('Ext.data.Store', {
model: 'Golfer',
data : data()});
return golferStore;
}
//end Local Store
Ext.onReady(function () {
var chart = Ext.create('Ext.chart.Chart', {
style: 'background:#fff',
animate: true,
shadow: true,
store: store1(),
legend: {
position: 'right'
},
axes: [{
type: 'Numeric',
position: 'bottom',
fields: ['golfClubs.Weight']
}, {
type: 'Category',
position: 'left',
fields: ['GolferName'],
title: 'Golfer'
}],
series: [{
type: 'bar',
axis: ['bottom'],
xField: ['golfClubs.Weight'],//Is that how you bind to child record?
yField: ['GolferName']
}]
});
var win = Ext.create('Ext.Window', {
width: '100%',
height: '100%',
title: 'Breakdown of Golfers and their Clubs..',
autoShow: true,
layout: 'fit',
items: chart
});
});
Cheers,
Tom.
This has been answered in the Sencha forum in case anyone was interested with this question.
The chart's store will need to have all of the data / records populated within the store (-vs- the store being populated with parent records with each having its own associated set of child records). So, I'm afraid what you're wanting to do you can't do directly.
Answer from Sencha Support Team Member
The solution seems to be the merging of parent and child Model's fields into a new Model that can be used in the chart's store combined with groupField and extending series.
Ext.define('Result', {
extend: 'Ext.data.Model',
fields: [
{ name: 'state', type: 'string', mapping:0 },
{ name: 'product', type: 'string', mapping:1 },
{ name: 'quantity', type: 'int', mapping:2 }
]
});
var store = Ext.create('Ext.data.ArrayStore', {
model: 'Result',
groupField: 'state',
data: [
['MO','Product 1',50],
['MO','Product 2',75],
['MO','Product 3',25],
['MO','Product 4',125],
['CA','Product 1',50],
['CA','Product 2',100],
['WY','Product 1',250],
['WY','Product 2',25],
['WY','Product 3',125],
['WY','Product 4',175]
]
});
Ext.define('Ext.chart.series.AutoGroupedColumn', {
extend: 'Ext.chart.series.Column',
type: 'autogroupedcolumn',
alias: 'series.autogroupedcolumn',
gField: null,
constructor: function( config ) {
this.callParent( arguments );
// apply any additional config supplied for this extender
Ext.apply( this, config );
var me = this,
store = me.chart.getStore(),
// get groups from store (make sure store is grouped)
groups = store.isGrouped() ? store.getGroups() : [],
// collect all unique values for the new grouping field
groupers = store.collect( me.gField ),
// blank array to hold our new field definitions (based on groupers collected from store)
fields = [];
// first off, we want the xField to be a part of our new Model definition, so add it first
fields.push( {name: me.xField } );
// now loop over the groupers (unique values from our store which match the gField)
for( var i in groupers ) {
// for each value, add a field definition...this will give us the flat, in-record column for each group
fields.push( { name: groupers[i], type: 'int' } );
}
// let's create a new Model definition, based on what we determined above
Ext.define('GroupedResult', {
extend: 'Ext.data.Model',
fields: fields
});
// now create a new store using our new model
var newStore = Ext.create('Ext.data.Store', {
model: 'GroupedResult'
});
// now for the money-maker; loop over the current groups in our store
for( var i in groups ) {
// get a sample model from the group
var curModel = groups[ i ].children[ 0 ];
// create a new instance of our new Model
var newModel = Ext.create('GroupedResult');
// set the property in the model that corresponds to our xField config
newModel.set( me.xField, curModel.get( me.xField ) );
// now loop over each of the records within the old store's current group
for( var x in groups[ i ].children ) {
// get the record
var dataModel = groups[ i ].children[ x ];
// get the property and value that correspond to gField AND yField
var dataProperty = dataModel.get( me.gField );
var dataValue = dataModel.get( me.yField );
// update the value for the property in the Model instance
newModel.set( dataProperty, dataValue );
// add the Model instance to the new Store
newStore.add( newModel );
}
}
// now we have to fix the axes so they work
// for each axes...
me.chart.axes.each( function( item, index, len ) {
// if array of fields
if( typeof item.fields=='object' ) {
// loop over the axis' fields
for( var i in item.fields ) {
// if the field matches the yField config, remove the old field and replace with the grouping fields
if( item.fields[ i ]==me.yField ) {
Ext.Array.erase( item.fields, i, 1 );
Ext.Array.insert( item.fields, i, groupers );
break;
}
}
}
// if simple string
else {
// if field matches the yField config, overwrite with grouping fields (string or array)
if( item.fields==me.yField ) {
item.fields = groupers;
}
}
});
// set series fields and yField config to the new groupers
me.fields,me.yField = groupers;
// update chart's store config, and re-bind the store
me.chart.store = newStore;
me.chart.bindStore( me.chart.store, true );
// done!
}
})
Ext.create('Ext.chart.Chart', {
renderTo: Ext.getBody(),
width: 500,
height: 300,
animate: true,
store: store,
legend: {
position: 'right'
},
axes: [
{
type: 'Numeric',
position: 'left',
fields: ['quantity'],
label: {
renderer: Ext.util.Format.numberRenderer('0,0')
},
title: 'Quantity',
grid: true,
minimum: 0
},
{
type: 'Category',
position: 'bottom',
fields: ['state'],
title: 'State'
}
],
series: [
{
type: 'autogroupedcolumn',
axis: 'left',
highlight: true,
xField: 'state',
yField: 'quantity',
gField: 'product'
}
]
});
Please see here
And as Sencha Fiddle for trying it out: https://fiddle.sencha.com/#fiddle/207

Resources