This component allows you to render a component or a series of components in one or more grid cells. Document and guide how to use are included in the component source code.
Latest version: 2.3 (released on 2012-05-05)
Ext compatible versions: Ext 4.0.7, Ext 4.1.0-rc3, Ext 4.1.0-gpl.
Below is the screenshot of grid using this component to render some kinds of components, such as: chart, combobox, spinner, date picker, checkbox, button.
grid-component.jpg
Change log
2012-01-14: Release version 1.0
2012-01-24: Release version 2.0 with the following updates:
- Remove relayEvents, eventPrefix and prefixEvent from component config which can be replaced by bubbleEvents.
- Add ownerCt to reflect component's owner container.
- Item config now can be a function.
- Fix the destroy()method.
2012-03-17: Release version 2.1with the following updates:
- Append record and store to event arguments
- Update internal code
2012-04-23: Release version 2.2 with the following updates:
2012-05-05: Release version 2.3 with the following updates:
Source code
Code:
/**
* @class Its.grid.column.Component
* @extends Ext.grid.column.Column
* @xtype itscomponentcolumn
* @version 2.3
* @author Nguyen Truong Sinh (vietits@yahoo.com)
*
* A column definition class which renders a component, or a series of components in a grid cell.
*
* @example
* var store = Ext.create('Ext.data.Store', {
* fields:['taskname', 'status', 'assignTo', 'dep'],
* data:[
* {taskname:"Task 1", status:1, assignTo:"Scott", dep:"Manangement"},
* {taskname:"Task 2", status:2, assignTo:"John", dep:"Sales"},
* {taskname:"Task 3", status:2, assignTo:"Smith", dep:"Accounting"},
* {taskname:"Task 4", status:3, assignTo:"Smith", dep:"Accounting"}
* ]
* });
*
* Ext.create('Ext.grid.Panel', {
* title: 'Component Column Demo',
* name : 'task',
* store: store,
* columns: [{
* .......
* },{ // a column with a combobox
* xtype: 'itscomponentcolumn',
* text : 'Status',
* width: 160,
* name : 'status',
* dataIndex: 'status',
* items: {
* prepare: function(config, value) {
* return {
* xtype: 'combobox',
* store: [[1,'In Queue'], [2,'Handling'], [3,'Complete']],
* value: value
* };
* }
* }
* },{ // a column with two buttons to modify/delete task
* xtype: 'itscomponentcolumn',
* align: 'center',
* width: 50,
* name : 'action',
* defaults: { // default configs applied for all items
* xtype: 'button',
* width: 'auto'
* },
* items: [{
* iconCls: 'icon-modify',
* action : 'modify',
* tooltip: 'Modify this task'
* },{
* iconCls: 'icon-delete',
* action : 'delete',
* tooltip: 'Delete this task'
* }]
* }],
* width: 400,
* renderTo: Ext.getBody()
* });
*
* # Default settings
*
* - hideable: false
* - groupable: false
* - defaultType: 'component'
*
* # Child component events
*
* The following arguments will be added to the end of argument list of each event:
*
* - record The record providing the data.
* - rowIndex The row index.
* - colIndex The column index.
* - store The store which provides model data
* - view The grid view object.
* - comp The component itself.
*
* @update 2012-01-14 22:37:21
* Release version 1.0
*
* @update 2012-01-24 08:37:09
* Release version 2.0 with the following updates:
* - Remove relayEvents, eventPrefix and prefixEvent from component config which can be replaced by bubbleEvents and bubblePrefix.
* - Add ownerCt to reflect component's owner container.
* - Add the ability that is an item config can be a function.
* - Fix the destroy() method.
*
* @update 2012-03-17 21:23:34
* Release version 2.1 with the following updates:
* - Append record and store to event arguments
* - Update internal code
* @update 2012-04-23 19:48:46
* Release version 2.2 with the following updates:
* - Update internal code
* @update 2012-05-05 09:58:07
* Release version 2.3 with the following updates:
* - Update internal code
*/
Ext.define('Its.grid.column.Component', {
extend: 'Ext.grid.column.Column',
alias : 'widget.itscomponentcolumn',
/**
* @cfg {String/Function/Object/String[]/Function[]/Object[]} items
* Child component configs. Items can be:
* - an xtype (string) or array of xtype (string[])
* - a function which returns item config object or an array of functions.
* Function will be called with following arguments:
* + value The value of the column's configured field (if any).
* + record The record providing the data.
* + rowIndex The row index.
* + colIndex The column index.
* + store The store which is providing the data Model.
* + view The current view.
* - an object which represents item conig or an array of config objects
* - mix of above types.
*
* Besides the normal component configs, each item may contain:
*
* @cfg {Function} items.prepare A function which returns the component config.
* @cfg {Object} items.prepare.config The current config.
* @cfg {Object} items.prepare.value The value of the column's configured field (if any).
* @cfg {Ext.data.Model} items.prepare.record The record providing the data.
* @cfg {Number} items.prepare.rowIndex The row index.
* @cfg {Number} items.prepare.colIndex The column index.
* @cfg {Ext.data.Store} items.prepare.store The store which is providing the data Model.
* @cfg {Ext.grid.View} items.prepare.view The current view.
*
* @cfg {Object} items.scope The scope (`this` reference) in which the `prepare` function
* is executed.
*/
constructor: function(config) {
var me = this;
var cfg = Ext.apply({
hideable: false,
groupable: false,
defaultType: 'component'
}, config);
var lst = cfg.items;
var def = cfg.defaults;
delete cfg.items;
delete cfg.columns;
delete cfg.defaults;
me.callParent([cfg]);
me.defaults = def;
me.queue = {};
me.comps = Ext.create('Ext.util.MixedCollection', false, me.getComponentId);
me.itemsConfig = [];
lst = Ext.isArray(lst) ? lst : [lst];
Ext.Array.each(lst, function(itm) {
if(Ext.isString(itm)) {
itm = {xtype: itm};
}
if(Ext.isObject(itm) || Ext.isFunction(itm)) {
me.itemsConfig.push(itm);
}
});
me.renderer = function(value, meta, record, rowIdx, colIdx, store, view) {
var ret = '';
var src = me.renderer.caller;
if (src.$owner && src.$owner.xtype == 'headercontainer') {
var iid = me.getIdPrefix(record);
me.queue[iid] = {
view : view,
store : store,
value : value,
record: record,
rowIdx: rowIdx,
colIdx: colIdx
};
ret = (Ext.isFunction(cfg.renderer) ? cfg.renderer.apply(cfg.scope || me, arguments) || '' : '')
+ '<div id="' + iid + '"> </div>';
}
return ret;
};
},
onRender: function() {
var me = this;
var pnl = me.up('tablepanel');
var view= pnl.getView();
me.mon(view, 'refresh' , me.injectItems, me);
me.mon(view, 'itemadd' , me.injectItems, me);
me.mon(view, 'itemupdate', me.injectItems, me);
me.callParent(arguments);
},
getIdPrefix : function(record) {
return Ext.String.format('{0}-{1}', this.getId(), record.internalId);
},
injectItems: function() {
var me = this;
var queue = me.queue;
var items = me.itemsConfig;
var cfLen = items.length;
me.queue = {};
for(var iid in queue) {
var itm = queue[iid];
var elm = Ext.get(iid);
if(elm) {
for(var idx = 0; idx < cfLen; idx++) {
var cfg = Ext.clone(items[idx]) || {};
if(Ext.isFunction(cfg)) {
cfg = cfg(itm.value, itm.record, itm.rowIdx, itm.colIdx, itm.store, itm.view);
}
if(Ext.isFunction(cfg.prepare)) {
cfg = cfg.prepare.call(cfg.scope || cfg, cfg, itm.value, itm.record, itm.rowIdx, itm.colIdx, itm.store, itm.view);
}
delete cfg.prepare;
cfg = me.applyDefaults(cfg);
cfg.itemId = iid + '-' + idx;
me.removeItem(cfg.itemId);
var cmp = me.lookupComponent(cfg);
if(cmp && cmp.isComponent) {
cmp.fireEvent = Ext.bind(cmp.fireEvent, cmp, [itm.record, itm.rowIdx, itm.colIdx, itm.store, itm.view, cmp], true);
cmp.render(elm.parent(), elm);
if (Ext.isIE6) {
elm.parent().repaint();
}
cmp.ownerCt = me;
me.comps.add(cmp);
}
}
elm.remove();
}
}
},
onItemRemove: function(record) {
var me = this;
var iid = me.getIdPrefix(record);
var idx = 0;
while(me.removeItem(iid + '-' + idx)) {
idx++;
}
},
removeItem: function(cmp) {
var me = this;
var ret = false;
if(Ext.isString(cmp)) {
cmp = me.comps.getByKey(cmp);
}
if(cmp) {
cmp.ownerCt = null;
me.comps.remove(cmp);
cmp.destroy();
ret = true;
}
return ret;
},
$callParent: function(args) {
var me = this;
var ret = null;
var method = me.$callParent.caller,
parentClass, methodName;
if (!method.$owner) {
method = method.caller;
}
parentClass = method.$owner.superclass;
methodName = method.$name;
if(me.items === me.comps) {
ret = parentClass[methodName].apply(me, args || []);
} else {
var items = me.items;
me.items = me.comps;
ret = parentClass[methodName].apply(me, args || []);
me.items = items;
}
return ret;
},
beforeDestroy: function() {
var me = this;
me.$callParent(arguments);
Ext.destroyMembers(me, 'comps', 'queue', 'renderer');
},
doRemove: function(cmp) {
this.$callParent(arguments)
},
removeAll: function() {
return this.$callParent(arguments);
},
getComponent: function() {
return this.$callParent(arguments);
},
getRefItems: function() {
return this.$callParent(arguments);
},
cascade: false
});
Example
Code:
Ext.require([
'Ext.picker.*',
'Ext.form.*',
'Ext.grid.*',
'Ext.data.*',
'Ext.panel.*',
'Ext.chart.*',
'Ext.chart.axis.Gauge',
'Ext.chart.series.*',
'Its.grid.column.Component'
]);
Ext.onReady(function() {
var store = Ext.create('Ext.data.Store', {
fields:['taskname', 'status', 'done', 'assignTo', 'dep', 'date', {name:'qty', type: 'int'}],
data:[
{taskname:"Task 1", status:1, done: 10, assignTo:"Scott", dep:"Manangement", date: '2012-01-01', qty:20},
{taskname:"Task 2", status:2, done: 30, assignTo:"John", dep:"Sales", date: '2012-01-12', qty:35},
{taskname:"Task 3", status:2, done: 50, assignTo:"Smith", dep:"Accounting", date: '2012-01-21', qty:12},
{taskname:"Task 4", status:3, done: 70, assignTo:"Smith", dep:"Accounting", date: '2012-01-05', qty:51}
]
});
Ext.create('Ext.grid.Panel', {
title: 'Component Column Demo',
name : 'task',
store: store,
width: 900,
height: 950,
renderTo: Ext.getBody(),
columns: [{
text: 'Task name',
flex: 1,
dataIndex: 'taskname'
},{ // chart column
xtype: 'itscomponentcolumn',
text : 'Chart',
width: 200,
dataIndex: 'done',
items: function(value) {
return {
xtype: 'chart',
width: 200,
height: 150,
style: 'background:#fff',
animate: {
easing: 'elasticIn',
duration: 1000
},
store: Ext.create('Ext.data.Store', {
fields: ['gauge'],
data : [{gauge: value}]
}),
insetPadding: 25,
flex: 1,
axes: [{
type: 'gauge',
position: 'gauge',
minimum: 0,
maximum: 100,
steps: 10,
margin: -10
}],
series: [{
type: 'gauge',
value: value,
field: 'gauge',
donut: false,
colorSet: ['#F49D10', '#ddd']
}]
}
}
},{ // column with a combobox
xtype: 'itscomponentcolumn',
text : 'Combobox',
name : 'status',
width: 160,
dataIndex: 'status',
items: function(value) {
return {
xtype: 'combobox',
store: [[1,'In Queue'], [2,'Handling'], [3,'Complete']],
value: value,
width: 130
};
}
},{ // column with a spinner
xtype: 'itscomponentcolumn',
text : 'Spinner',
width: 50,
dataIndex: 'qty',
items: function(value) {
return {
xtype: 'spinnerfield',
value: value,
width: 25
};
}
},{ // column with date picker
xtype: 'itscomponentcolumn',
text : 'Date picker',
dataIndex: 'date',
width: 200,
items: function(value) {
return {
xtype: 'datepicker',
width: 190,
value: new Date(value)
};
}
},{ // column with checkboxes
xtype: 'itscomponentcolumn',
text : 'Checkbox',
defaultType: 'checkboxfield',
items: [{
boxLabel : 'Anchovies',
name : 'topping',
inputValue: '1',
}, {
boxLabel : 'Artichoke Hearts',
name : 'topping',
inputValue: '2',
checked : true,
}, {
boxLabel : 'Bacon',
name : 'topping',
inputValue: '3',
}]
},{ // column with two buttons
xtype: 'itscomponentcolumn',
text : 'Buttons',
align: 'center',
width: 60,
name : 'action',
sortable: false,
defaults: { // default configs applied for all items
xtype: 'button',
width: 'auto'
},
items: [{
iconCls: 'icon-modify',
action : 'modify',
tooltip: 'Modify this task'
},{
iconCls: 'icon-delete',
action : 'delete',
tooltip: 'Delete this task'
}]
}]
});
});