Hi all,

Here's a combobox, to be used in an editable grid, that can handle the following data structure:

Code:
{ 
  rows: [{
        id: 1,
        name: 'Twensoc',
        role: {                <-- Notice that the role is a parent of the 'user'/'row'
            id: 1,                and is defined as an object 
            name: 'admin'
        }
    }]
}
It took me a while to get this right, so I thought I might as well share it with you. Please see the comments in the code for the complete explanation.

Code:
/**
 * @class Ext.twensoc.StaticParentCombo 
 * @extends Ext.grid.GridPanel
 * 
 * <p>This is an inherited version of the Ext.form.ComboBox, meant to be used in 
 * an editable grid. My java FlexJson library returns the json mentioned below,
 * and that json is incompatible with the structure expected by the default
 * ext combobox. </p>
 * <p>This combobox can handle only static data, and therefore it is suitable for selecting
 * static data (ie it won't request data from the server, but expects the choices to be defined).</p>
 * <p>The sole purpose of this component is to be able to use
 * the following data structure in your record:</p>
 * <pre><code>
 * { 
 *   rows: [{
 *       id: 1,
 *       name: 'Twensoc',
 *       role: {                <-- Notice that the role is a parent of the 'user'/'row'
 *           id: 1,                and is defined as an object 
 *           name: 'admin'
 *       }
 *   }]
 * } 
 * </pre></code>
 * <p>There's no role_id in the record itself. Instead, the 'role' field is an object,
 * containing an id and a name. This combo can handle the 'role' field, and will display
 * the 'name' field of the object (ie role.name). You can use it in your grid's column model like this:</p>
 * <pre><code>
 * grid.cm = new Ext.grid.ColumnModel([
 *        {name:'id', id:'id', header:'Id', dataIndex:'id'},
 *        {name:'name', header:'name', dataIndex:'name', width: 200, editor: new Ext.form.TextField({allowBlank:false}) },
 *        {name:'role.name', header: 'Role', dataIndex:'role',
 *            editor: new Ext.twensoc.ParentRelationCombo({
 *                recordField: 'role',
 *                data: [[1, 'guest'], [99, 'admin']]
 *            }), 
 *            renderer: function(v,f,r) {
 *                return r ? r.data.role.name : '';
 *            }
 *        }
 * ]);
 * </pre></code>
 * <p>The mapping is simple:</p>
 * <pre><code>
 * this.mapping = [
 *         {name:'id'},
 *        {name:'name'},
 *        {name:'role'} // will contain an object: {id: x, name: y}
 * ];
 * </pre></code>
 * <p>Don't forget to create the namespace!</p>
 * <p>Ext.twensoc.StaticParentCombo is licensed under the terms of the Open Source GPL 3.0 license.</p> 
 * 
 * @constructor
 * @param {string} recordField Name of the field that contains the parent data.
 * @param {data} data Data to choose from. Example: [[1, 'guest'], [99, 'admin']]
 */

// Remove comments if this namespace is not yet defined.
//Ext.namespace('Ext.twensoc');

Ext.twensoc.StaticParentCombo = Ext.extend(Ext.form.ComboBox, {
    typeAhead: true,
    triggerAction: 'all',
    lazyRender:true,
    mode: 'local',
    valueField: 'id',
    displayField: 'name',
    listClass: 'x-combo-list-small',
    /**
     * Initialization
     */
    initComponent: function(){
        this.store = new Ext.data.ArrayStore({
            id: 0,
            fields: this.fields || ['id','name'],
            data: this.data
        });
        Ext.twensoc.StaticParentCombo.superclass.initComponent.call(this);
        this.on('beforerender', this.onBeforeRender, this);
    },
    /**
     * Called once. Attach an event listener to the 'complete' event.
     */
    onBeforeRender: function(combo) {
        this.gridEditor.on("complete", this.onEditComplete, this);
    },
    /**
     * Because the gridEditor's onEditComplete method assumes either an int or a string value,
     * (and we have an object here), the 'String(value) !== String(startValue)' check will always fail.
     * Therefore, we've attached another listener, who's sole purpose is to set the record's field value
     * and mark that field as modified.
     */
    onEditComplete : function(ed, value, startValue) {
        ed.record.set(this.recordField, this.value);
    },
    /**
     * Override the combo's onSelect method in order to change the this.setValue call.
     */
    onSelect : function(record, index){
        if(this.fireEvent('beforeselect', this, record, index) !== false){
            this.setValue({id:record.data.id, name:record.data.name});
            this.collapse();
            this.fireEvent('select', this, record, index);
        }
    },
    /**
     * Override the setVaue method. The v(alue) parameter is expected to be an object
     * like in {id: 1, name:'combotext'}
     */
    setValue : function(v){
        this.lastSelectionText = v.name;
        Ext.form.ComboBox.superclass.setValue.call(this, v.name); // hack: skip the Ext.Form.Combobox.setValue
        this.value = v;
        return this;
    },
    /**
     * The this.value variable contains the object (' {id: 1, name:'combotext'} '),
     * and we should either return that or an empty string.
     */
    getRawValue : function() {
        var v = this.value ? this.value : '';
        if (v === this.emptyText) {
            v = '';
        }
        return v;
    },
    /**
     * This method is either called with a string paramter or an object parameter,
     * and is used to process the value before calling the validate method. 
     * The validate method expects a string, so always return a string here.
     */
    processValue : function(v){
        if(Ext.isObject(v)) {
            return v.name
        }
        return v;
    }
});
Any comments or improvements are welcome!

Best regards,
Ronaldo