To people who dogmatically refuse to use tables to arrange forms, I say PAH!

Sometimes you want rows of input, each of which has more than one input field on.

In fact most forms I've ever written look like that.

With raw Ext, you have to create a Container which uses layout: 'table'. This creates a new table inside the Container's rendered element.

Then in each cell, you have to create a new Container with layout:'form' so that the Fields get rendered with labels.

All of which is a pain, and creates DOM clutter.

This class is a Container who's main element is a table, which is used by its TableLayout instance - no extra DOM layers there.

The cells created by the table layout manager are used as Containers, so no extra layer of DOM required there either.

To use it:

Code:
    new Ext.FormPanel({
        title: 'Tabular input',
        layout: 'anchor',
        height: 400,
        width: 600,
        padding: 10,
        items: {
            xtype: 'tablecontainer',
            columns: 2,         // Number of columns. Defaults to 1
            anchor: '100%',
            cellLayout: 'form', // Layout config for each <td>
            items: [
                { xtype: 'textfield', fieldLabel: 'Text field 1' },
                { xtype: 'textfield', fieldLabel: 'Text field 2' },
                { xtype: 'datefield', fieldLabel: 'Date field 1' },
                { xtype: 'datefield', fieldLabel: 'Date field 2' },
                { xtype: 'numberfield', fieldLabel: 'Number field 1' },
                { xtype: 'numberfield', fieldLabel: 'Number field 2' }
            ]
        },
        renderTo: document.body
    });
Which gives you



The class:

Code:
Ext.TableContainer = Ext.extend(Ext.Container, (function(){
    function getCellSize() {
        return { width: this.dom.clientWidth, height: this.dom.clientHeight };
    }
    return {
        firstRowClass: "x-table-layout-first-row",

        lastRowClass: "x-table-layout-last-row",

        constructor: function(config) {
            if (Ext.isObject(config.layout)) {
                config.layout.type = 'table'
            } else {
                config.layout = {
                    type: 'table'
                }
            }
            config.layout.columns = config.layout.columns || config.columns || 1;
            Ext.TableContainer.superclass.constructor.apply(this, arguments);
        },
    
        onRender: function() {
            Ext.apply(this.layout, {
                table: this.el = document.createElement('table'),
                renderItem: this.renderItem
            });
            this.el.id = this.getId();
            this.el.appendChild(document.createElement('tbody'));
            Ext.TableContainer.superclass.onRender.apply(this, arguments);
        },

        onResize: function() {
            this.el.dom.style.tableLayout = 'fixed';
            Ext.TableContainer.superclass.onResize.apply(this, arguments);
        },
    
        onLayout: function() {
            Ext.TableContainer.superclass.onLayout.apply(this, arguments);
            var trs = this.el.select("tr");
            trs.removeClass([this.firstRowClass, this.lastRowClass]);
            trs.item(0).addClass(this.firstRowClass);
            trs.item(trs.getCount() - 1).addClass(this.lastRowClass);
        },
    
        renderItem : function(c, position, target){
            var w, container, idx, cellEl;
            if(c && !c.rendered){
                if (Ext.isNumber(w = c.cellWidth)) {
                    if (w < 1) {
                        w = (w * 100) + '%'
                    } else {
                        w = w + 'px';
                    }
                }
                idx = this.container.items.indexOf(c);
                this.container.remove(c, false);
                cellEl = this.getNextCell(c);
                if (w) {
                    cellEl.style.width = w;
                }
                cellEl.id = 'ext-comp-' + (++Ext.Component.AUTO_ID);
                cellEl = Ext.get(cellEl);
                cellEl.getStyleSize = getCellSize;
                container = new Ext.Container(Ext.apply({
                    ownerCt: this.container,
                    el: cellEl,
                    id: cellEl.id,
                    layout: this.container.cellLayout,
                    items: c
                }, c.cellLayout));
                Ext.form.FormPanel.prototype.applySettings(container);
                this.configureItem(c, position);
                this.container.insert(idx, container);
                container.render(cellEl.dom.parentNode);
            }
        }
    };
})());
Ext.reg('tablecontainer', Ext.TableContainer);