!!! UPDATED !!! Sencha Touch Grid: Percentage based width

Dienstag, 1. Juli 2014
As many of you I was wandering why Sencha didn't include the possibility to set a column width to a specified 25% of the screen. So here is a how to enable this feature by yourself.

DEMOhttps://dl.dropboxusercontent.com/u/2056172/KeepAlive/GridPrecentageWidth/ExampleProject/index.html

Full Project: https://dl.dropboxusercontent.com/u/2056172/KeepAlive/GridPrecentageWidth/GridColumnPercentage.zip

Full Tutorial:

First we need a sample grid and set the  width of the columns to a percentage value, for example '25%'

Ext.define('ExampleProject.view.SampleGrid', {
    extend: 'Ext.grid.Grid',
    alias: 'widget.samplegrid',

    requires: [
        'Ext.grid.column.Column'
    ],

    config: {
        height: '100%',
        id: 'samplegrid',
        store: 'MyStore',
        title: 'MyGrid',
        columns: [
            {
                xtype: 'column',
                itemId: 'column1',
                width: '40%',
                dataIndex: 'data1',
                text: 'Column1'
            },
            {
                xtype: 'column',
                itemId: 'column2',
                width: '25%',
                dataIndex: 'data2',
                text: 'Column2'
            },
            {
                xtype: 'column',
                itemId: 'column3',
                width: '25%',
                dataIndex: 'data3',
                text: 'Column3'
            }
        ],
        listeners: [
            {
                fn: 'onGridPainted',
                event: 'painted'
            },
            {
                fn: 'onGridWidthChange',
                event: 'widthchange'
            }
        ]
    
    }
)};

All we need to to now is to call a function on the painted event which will let us render the percentage value to a pixel value and overwrite the width on the column.

So add a painted listener because we need to set the new size when the grid is painted:


onGridPainted: function(element, eOpts) {
        this.renderColumnPercentage(element);
},

And now calculate the pixels from the percentage value:

var grid = this,
    
    columnArr = grid.getColumns(),
    
    numberOfCols = columnArr.length,
    
    clientWidth = element.getAttribute('clientWidth');

columnArr.forEach(
    function(column, index, array) {
        
        var perWidth = column.getWidth(); // Percentage or Pixel width --> '25%' || '123'
        
        if (!Ext.isNumeric(perWidth) && perWidth) { // Checking for a percentage value
            
            perWidthNum = perWidth.substr(0,perWidth.length-1) / 100; // Numeric Width --> 0.25
            pxWidth = clientWidth * perWidthNum; // Width in Pixel --> 123
            
        }
        
        // Header width
        column.bodyElement.setWidth(pxWidth);
        column.refreshSizeState();
        
        // Each column has its own class
        if (! column.getCellCls()) {
            column.setCellCls(createCellCssClass(pxWidth));
        } else {
            // Query doms for a class --> 1 Class = 1 Column
            var elements = Ext.query('.' + column.getCellCls()),
                newClass = createCellCssClass(pxWidth),
                oldClass = column.getCellCls();
            
            // Loop doms and replace old class with new one
            Ext.Array.each(elements, function(ele, index, elementsItSelf) {
                
                ele.classList.remove(column.getCellCls());
                ele.classList.add(newClass);
                
            });
            
            column.setCellCls(newClass);
            
        }
        
    }
);

function createCellCssClass(pxWidth) {
    
    var className = 'cell-' + 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
    
    var css = document.createElement('style');
    css.type = 'text/css';
    
    var styles = '.' + className + ' { width: ' + pxWidth + 'px !important; }';
    
    if (css.styleSheet)
        css.styleSheet.cssText = styles;
    else
        css.appendChild(document.createTextNode(styles));
    
    document.getElementsByTagName("head")[0].appendChild(css);

    return className;
}

The width is set on the elements level to prevent loosing the percentage value on the column level.

Last we need to call the same function when widthchanged is fired on the grid itself because this will need resizing for all columns.
Just add a listener to perform the resizing again:

onGridWidthChange: function(component, value, oldValue, eOpts) {
        this.renderColumnPercentage(component.bodyElement);
},