1. #1
    Sencha - Community Support Team
    Join Date
    Jan 2012
    Posts
    1,376
    Vote Rating
    113
    vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all

      1  

    Default Its.grid.column.Component

    Its.grid.column.Component


    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:
    • Update internal code
    2012-05-05: Release version 2.3 with the following updates:
    • Update internal code
    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 + '">&#160;</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'
                    }]
              }]
          });
    });
    Last edited by vietits; 4 May 2012 at 7:09 PM. Reason: Release version 2.3

  2. #2
    Sencha - Senior Forum Manager mitchellsimoens's Avatar
    Join Date
    Mar 2007
    Location
    Gainesville, FL
    Posts
    36,620
    Vote Rating
    817
    mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute

      0  

    Default


    Does it work for 4.1.0?
    Mitchell Simoens @SenchaMitch
    Sencha Inc, Senior Forum Manager
    ________________
    Check out my GitHub, lots of nice things for Ext JS 4 and Sencha Touch 2
    https://github.com/mitchellsimoens

    Think my support is good? Get more personalized support via a support subscription. https://www.sencha.com/store/

    Need more help with your app? Hire Sencha Services services@sencha.com

    Want to learn Sencha Touch 2? Check out Sencha Touch in Action that is in print!

    When posting code, please use BBCode's CODE tags.

  3. #3
    Sencha - Community Support Team
    Join Date
    Jan 2012
    Posts
    1,376
    Vote Rating
    113
    vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all

      0  

    Default Re:

    Re:


    Hi Mitchell,

    Because of some basic differences between Ext 4.0.7 and Ext 4.1.0-beta-1 so I am sure this component will not work on 4.1.0 if we won't adjust some things.

    This component uses method `relayEvents` to relay events from child components. However, the implementations of `relayEvents` in these versions of Ext are different:
    • with 4.0.7: event names must be prefixed with eventPrefix before calling method `relayEvents`.
    • with 4.1.0: event names will be prefixed with eventPrefix by method `relayEvents`.
    This feature will be affected if you want to relay events from child components and capture them at the column component. If it is the case, you must adjust the configs (in this case these are prefixEvent and relayEvents) of child components. Below is one such example:

    With 4.0.7
    Code:
    {
        xtype: 'itscomponentcolumn',
        items: [{
            xtype: 'button',
            relayEvents: ['click'],
            eventPrefix: 'button',
            prefixEvent: true // the component will prefix each event name with eventPrefix before calling relayEvents
        }]
        ...
    }
    With 4.1.0-beta-1
    Code:
    {
        xtype: 'itscomponentcolumn',
        items: [{
            xtype: 'button',
            relayEvents: ['click'],
            eventPrefix: 'button',
            prefixEvent: false // the component won't prefix event names with eventPrefix
        }]
        ...
    }

  4. #4
    Sencha Premium Member skirtle's Avatar
    Join Date
    Oct 2010
    Location
    UK
    Posts
    3,501
    Vote Rating
    286
    skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future

      0  

    Default


    This is very interesting. It's attempting to solve the same problem as my own Component Column but the approach is quite different:

    http://www.sencha.com/forum/showthread.php?148064

    I need to study your code in more detail to understand better how it works. I see a few things that look very similar to my own code but other sections, like the event relaying, that are totally different.

  5. #5
    Sencha - Community Support Team
    Join Date
    Jan 2012
    Posts
    1,376
    Vote Rating
    113
    vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all

      0  

    Default


    Hi Skirtle,

    I'm happy that you are interested in my component. Hope that it is easy for you to understand how it works. To help you easier in understanding my component, here are some briefs of its features:
    • It can be appears one or more times in a grid.
    • It can contain one or more components in one column. Components can be the same xtype or not.
    • It appends cell information (rowIndex, colIndex) and others (view, itself) to the end of each event argument of each component.
    • It attachs cell information (rowIndex, colIndex, view) to each component through the cellInfo property so you can refer to these in your code.
    • It can relay component events by setting respective configs (relayEvents, eventPrefix, prefixEvent)
    Though I developed this component for 4.0.7, but I have tested it with 4.1.0-beta-1 and it works fine, but with one thing to remember (as I mentioned in the ealier reply) is that you should set prefixEvent to false.

    I hope to get useful responses from you to improve this component more useful.

  6. #6
    Sencha - Community Support Team
    Join Date
    Jan 2012
    Posts
    1,376
    Vote Rating
    113
    vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all

      0  

    Default


    Release version 2.0 (updated on the first post of this thread) 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.

  7. #7
    Sencha User
    Join Date
    Apr 2012
    Posts
    57
    Vote Rating
    0
    NCN is on a distinguished road

      0  

    Default


    hi vietits[FONT='Helvetica Neue', Arial, 'Lucida Grande', sans-serif][/FONT]

    [FONT='Helvetica Neue', Arial, 'Lucida Grande', sans-serif] can you please upload your updated code which will work in our local sys. as well.[/FONT]

    [FONT='Helvetica Neue', Arial, 'Lucida Grande', sans-serif]I want the graph in grid cell for the sencha touch 2.

    Thanks in advance !!!
    [/FONT]

  8. #8
    Sencha - Community Support Team
    Join Date
    Jan 2012
    Posts
    1,376
    Vote Rating
    113
    vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all vietits is a name known to all

      0  

    Default Update to version 2.2

    Update to version 2.2


    I have updated Its.grid.column.Component to version 2.2 on the first post of this thread. Please see more details there.

  9. #9
    Sencha User
    Join Date
    Dec 2009
    Location
    Dallas, TX
    Posts
    20
    Vote Rating
    0
    ramana_l_v is on a distinguished road

      0  

    Default


    Hi vietits,
    This is very interesting, but I am having a very strange problem I am using 2.2 version of your code. If I add a textfield or textarea I am unable to enter space in those fields. Below is my code where I am just adding a textfield component, I am not sure what I am doing wrong.


    Code:
    Ext.define('iLab.view.ui.GenericDetailsGrid', {
       extend: 'Ext.grid.Panel',
    
    
        height: 650,
        hideHeaders: true,
        showTemplate: false,
        backButtonTitle: 'iLab',
        disableSelection : true,
        stripeRows: false,
        
        initComponent: function() {
            var me = this;
    
    
            Ext.applyIf(me, {
                columns: [
                    .....
                    {
                        xtype: 'itscomponentcolumn',
                        flex: 1,
                        dataIndex: 'type',
                        items: function(value, record, rowIndex, colIndex, store) {
                            return { 
                                xtype: 'textfield',
                                itemId: 'buzzReplyMessagee'
                            }; 
                        }
                    }                
                ],
                viewConfig: {
                    stripeRows: false
                }
    
    
            });
    
    
            me.callParent(arguments);
        }
        
    });
    Thanks in advance...

  10. #10
    Sencha Premium Member skirtle's Avatar
    Join Date
    Oct 2010
    Location
    UK
    Posts
    3,501
    Vote Rating
    286
    skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future skirtle has a brilliant future

      0  

    Default


    Some key presses are caught by the grid for navigation. Try adding this listener to your textfield:

    Code:
    listeners: {
        inputEl: {
            keydown: function(ev) {
                ev.stopPropagation();
            }
        }
    }