ExtJS - Grid and Editor Grid

extjs

http://vimeo.com/59611158
http://ahlearns.wordpress.com/2012/12/07/ext-js-4-two-ways-to-configure-selection-model-in-grid/
http://www.sencha.com/learn/ext-js-grids-with-php-and-sql
http://docs.sencha.com/extjs/3.4.0/#!/api/Ext.grid.ColumnModel-method-setColumnHeader

Excel:
http://www.lukehorvat.com/blog/excel-grid-plugin-for-ext-js-4/

Multi-columns sorting:
http://harrydeluxe.github.io/extjs-ux/example/grid/multiplesort.html
http://www.learnsomethings.com/2012/08/15/extjs-4-1-ux-that-will-allow-multiple-remote-sorting-on-columns-and-display-multiple-directional-icons/
https://github.com/jmcdonald69124/extjs-multisort-ux

Multi-columns Filtering:
http://existdissolve.com/2011/11/extjs-4-filtering-on-multiple-fields/
http://atechiediary.blogspot.com/2013/06/extjs-how-to-filter-data-of-store-in.html
http://extjs-tutorials-store.blogspot.com/
http://pastebin.com/ygDw4HKV
http://aboutfrontend.com/extjs/extjs-grid-filter/
http://www.sencha.com/forum/showthread.php?12185-Store-and-multiple-filters
http://stackoverflow.com/questions/7996028/how-to-filter-multiple-extjs-grid-columns
http://stackoverflow.com/questions/19999591/applying-multiple-filters-on-store-object-in-extjs
http://training.figleaf.com/tutorials/senchacomplete/chapter3/lesson5/2.cfm

  • How to find column definition given the column name?
  • How to hide a column programmatically given its name?
  • How to obtain a list of columns?

Example of simple grid, without selection model:

Ext.create('Ext.data.Store', {
    storeId: 'simpsonsStore',
    fields:[ 'name', 'email', 'phone'],
    data: [
        { name: 'Lisa', email: 'lisa@simpsons.com', phone: '555-111-1224' },
        { name: 'Bart', email: 'bart@simpsons.com', phone: '555-222-1234' },
        { name: 'Homer', email: 'homer@simpsons.com', phone: '555-222-1244' },
        { name: 'Marge', email: 'marge@simpsons.com', phone: '555-222-1254' }
    ]
});

Ext.create('Ext.grid.Panel', {
    title: 'Simpsons',
    store: Ext.data.StoreManager.lookup('simpsonsStore'),
    columns: [
        { text: 'Name', dataIndex: 'name' },
        { text: 'Email', dataIndex: 'email', flex: 1 },
        { text: 'Phone', dataIndex: 'phone' }
    ],
    height: 200,
    width: 400,
    renderTo: Ext.getBody()
});

What are available selection model?

  1. checkboxmodel
  2. rowmodel
  3. cellmodel
  4. treemodel

How can we position the buttons inside a bbar or tbar?

Use tbfill:

     tbar : [
         'Item 1',
         { xtype: 'tbfill' },
         'Item 4',
         { xtype: 'tbfill' },
         'Item 2'
     ],

In what order are the events triggered?

  1. render
  2. viewready
  3. afterrender
  4. boxready
  5. afterlayout (this may be invoked several times)

How can we override the checkbox selection model?

How can we conditionally enable or disable the row selection check box based on the values in the record?

How can we conditionally disable the editor for only certain cells?

afterrender: function(cmp,opts) {
    var grid = Ext.getCmp('webformGrid');
    var store = grid.getStore();
    var columns = grid.columns;
    var desiredColumn = grid.getView().getHeaderCt().child('#configSettingCol');
    var i = 0;
    var record, editor;
    var count = store.count();
    for (i = 0; i < count; i++) {
        record = store.getAt(i); 
        if (record.get('configurable').toLowerCase() != 'config') {
            editor = desiredColumn.getEditor(record,desiredColumn.dataIndex);
            editor.disable();
        }
    }
    console_log("afterrender invoked");
}

If we have a editing grid which contains a combo editor, how can we cause the 'edit' event to be triggered as soon as the user select an option?

When the user select an option, the combo editor still has focus, therefore it thinks that the user is still editing that cell. When the user select an option, the combo box receive the 'select' or 'change' event. From there, we can call the completeEdit() method.

var webformEditing = Ext.create('Ext.grid.plugin.CellEditing', {
    clicksToEdit: 1,
    pluginId: 'cellplugin',
    listeners: {
        scope: this,
        beforeedit: function(editor,e,eOpts) {
        },
        edit: function(editor, e, eOpts) {
        },
        validateedit: function(editor, e, eOpts) {
        }
    }
});

var grid = Ext.create('Ext.grid.Panel', {
    store: store,
    plugins: [webformEditing,bufferRenderer],
    columns: [
        {
            header: 'Configuration Setting',
            dataIndex: 'Current Facility Configuration',
            renderer: function(value, metaData, record, rowIdx, colIdx, store, view) {
                if (record.get('configurable').toLowerCase() != 'config') { 
                    return '<span class="noConfig">' + value + '</span>';
                } else {
                    return value;
                }
            },
            editor: new Ext.form.field.ComboBox({
                store: [
                    ['All Users - Mandatory','1. All Users - Mandatory'],
                    ['All Users - Optional','2. All Users - Optional'],
                    ['Managers Only - Mandatory','3. Managers Only - Mandatory'],
                    ['Managers Only - Optional','4. Managers Only - Optional'],
                    ['Deactivate','5. Deactivate']
                ],
                listeners: {
                    select: function(combo, records, eOpts) {
                        Ext.getCmp('saveAsDraftBtn').enable();
                        Ext.getCmp('cancelBtn').enable();
                        webformEditing.completeEdit();
                    }
                }
            })
        }
    ]
});

How can we find a column given its itemId?

var desiredColumn = grid.getView().getHeaderCt().child('#configSettingCol');

How can we prevent the cell editing plugin from automatically select the record?

var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', {
    clicksToEdit: 1,
    listeners: {
        beforeedit: function(editor,obj,options) {
            console_log("beforeedit event fired");
            if (globalEditingDisabled == true) {
                return false;
            }
            if (obj.record.get('configurable').toLowerCase() != 'config') {
                return false;
            }
            isEditorActive = ! isEditorActive;
        },
        edit: function(editor, e, eOpts) {
            isEditorActive = ! isEditorActive;
            console_log("edit event fired");
            if (e.value != e.originalValue) {
                Ext.getCmp('cancelBtn').enable();
                Ext.getCmp('saveAsDrafBtn').enable();
            }
        },
        validateedit: function(editor, e, eOpts) {
            console_log("validateedit event fired");
        }
    }
});

var grid = Ext.create('Ext.grid.Panel', {
    // width: '100%',
    height: mainViewportHeight - 140,
    frame: false,
    style: 'margin: 0px 0px',
    title: 'Testing',
    preventHeader: true,
    iconCls: 'icon-grid',
    renderTo: 'ViewportContainer',
    store: store,
    id: 'webformGrid',
    plugins: [cellEditing, bufferRenderer],
    selType: 'checkboxmodel',
    selModel: {
        checkOnly: true,
        injectCheckbox: 0,
        listeners: {
            beforeselect: function() {
                console_log("beforeselect event fired");
                if (isEditorActive) {
                    return false;
                }
            },
            select: showOrHideMultiEdit,
            deselect: showOrHideMultiEdit
        }
    },
    multiSelect: true,
    ...
});

The beforeedit is triggered on the next editable cell before the edit event is triggered on the old cell. This is not the typical expected behavior, but that is how it is. So to prevent the cell editing plugin from automatically select the row, I added the isEditorActive variable and use it to detect if editing is going on.

How can we hide the "check all" / "un-check all" check box in the header of the grid?

  • Use the configuration setting header:false
  • Use CSS: .x-column-header-checkbox{ display: none; }
  • Use the configuration setting: showHeaderCheckbox:false

When we have an editing grid, how can we configure the writer store to always send the records as an array?

allowSingle: false

Without the above setting in the writer store, and if the user only make change to one record, the writer store will send the record which is a JSON object, not a JSON array. This is fine if you only allow the user to modify and save one record at a time. However, if you allow the user to modify and save one or multiple records at the same time, your back-end code need to have logic to detect whether the front-end is sending a single record or multiple records. Luckily, with the above configuration, we do not have to deal with this situation in the back-end. Perhaps, ExtJS team can set this configuration setting to true by default.

How can we obtain a list of columns?

Use grid.columnManager.columns

How can we obtain the column given its itemId?

grid.getView().getHeaderCt().child('#myColumnItemId')

Why should we use grid.columnManager.columns instead of grid.columns?

Instead of grid.columns use grid.columnManager.columns. That way if the columns are reordered, your function returns the correct index.

How can we process selected records?

var selModel = Ext.getCmp('filterGrid').getSelectionModel();
var records =  selModel.getSelection();
var selectedValues = {};
var i;
var len = records.length;
var value, record;
for (i = 0; i < len; i++) {
    record = records[i];
    value = record.get(key);
    if (typeof(selectedValues[value]) == 'undefined') {
        typeof(selectedValues[value] = true;
    }
}

How can we detect user clicking on the header?

{
    xtype: 'grid',
    listeners: {
        'headerclick': {
            fn: function(grid, col, e){
                // whatever
            },
            scope: this
         }
    }
}

or 

myGrid.on('headerclick', function(grid, col, e){
    // whatever
});

How can we refresh the view?

grid.view.refresh();

How can we clear sorting?

grid.store.sorters.clear();

This alone does not reset the sort order back to the default sort order. This may not even remove the sort icon. You may need to refresh the view, and do additional thing.

Ext.getCmp('facSearchGrid').getStore().load({params: {start: 0, limit: 20}});

How can we reload a grid without going back to first page?

Ext.getCmp('facSearchGrid').getStore().reload();

How can we revert all changes done on the grid?

grid.getStore().rejectChanges();

How can we prevent editing of certain cell based on some logic?

Intercept the beforeedit event.

var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', {
    clicksToEdit: 1,
    listeners: {
        beforeedit: function(editor,obj,options) {
            if (obj.record.get('configurable').toLowerCase() != 'config') {
                return false;
            }
        }
    }
});

The code above works with ExtJS 4.0.7. The grid was created:

var grid = Ext.create('Ext.grid.Panel', {
    height: mainViewportHeight - 200,
    frame: false,
    style: 'margin: 0px 0px',
    preventHeader: true,
    iconCls: 'icon-grid',
    renderTo: 'ViewportContainer',
    store: store,
    id: 'webformGrid',
    plugins: [cellEditing, bufferRenderer],
    selType: 'checkboxmodel',
    selModel: {
        checkOnly: true,
        injectCheckbox: 0
    },
    multiSelect: true,
    columns: [{...},{...},{...}]
});

To prevent row editing:

grid.on('beforeedit', function(event) {
    if (event.row == 0) {
         this.store.rejectChanges();
         event.cancel = true;
     }
}, grid);

How can we change the column header?

Use the setColumnHeader method:

var cm = Ext.getCmp('yourGrid').getColumnModel();
cm.setColumnHeader(1, 'Here is the new column heading');

// Alternatively you can give your column an ID and use that instead:
{header: 'Original Column Header', id:'columnx', dataIndex:'blah'},

// THEN IN YOUR STORE LOAD EVENT
var cm = Ext.getCmp('yourGrid').getColumnModel();
cm.setColumnHeader(cm.getIndexById('columnx'), 'Here is the new column heading')

Editor inside a grid:

cm = grid.getColumnModel();
cm.getCellEditor(colIndex, rowIndex);
cm.setEditable(colIndex, true / false);
grid.focusCell(row, col);
grid.startEditing(rowIndex, colIndex);
grid.stopEditing();

Grid:

Ext.onReady(function() {
    // sample static data for the store
    var myData = [['Apple',29.89,0.24,0.81,'9/1 12:00am'],
        ['Ext',83.81,0.28,0.34,'9/12 12:00am'],
        ['Google',71.72,0.02,0.03,'10/1 12:00am'],
        ['Microsoft',52.55,0.01,0.02,'7/4 12:00am'],
        ['Yahoo!',29.01,0.42,1.47,'5/22 12:00am']
    ];

    // create the data store
    var ds = new Ext.data.ArrayStore({
        fields: [
           {name: 'company'},
           {name: 'price', type: 'float'},
           {name: 'change', type: 'float'},
           {name: 'pctChange', type: 'float'},
           {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
        ]
    });

    // manually load local data
    ds.loadData(myData);

    // create the colum Manager
    var colModel = new Ext.grid.ColumnModel([
            {header: 'Company', width: 160, sortable: true, dataIndex: 'company'},
            {header: 'Price', width: 75, sortable: true, dataIndex: 'price'},
            {header: 'Change', width: 75, sortable: true, dataIndex: 'change'},
            {header: '% Change', width: 75, sortable: true, dataIndex: 'pctChange'},
            {header: 'Last Updated', width: 85, sortable: true,
                renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
    ]);

    // create the Grid
    var grid = new Ext.grid.GridPanel({
        store: ds,
        colModel: colModel,
        height: 300,
        width: 600,
        title: 'My First Grid'
    });

    // render the grid to the specified div in the page
    grid.render('grid-example');
});
While this looks like a lot, it is really only seven lines of code in total! The first line of code creates an array of data to be displayed in the grid. In real projects, you would likely load this data from some dynamic source like a database or web service. Next, we create and load a data store, which will tell the underlying Ext library how to read and format the data. Next, we define our column model which simply allows us to set up configuration options for each column of the grid. Lastly, we create the grid widget, passing in the data store and column model and finally render it. If all went well, you should end up with something that looks close to this: IntroToExt_grid.gif

How can we get the DOM of the row?

yourGrid.getNode(yourGrid.getStore().getAt(rowIndex))

or you can use getNode directly but incase of any headerbar it may not work as supposed to:

yourGrid.getNode(rowIndex)

How do you get the rows from the grid?

var rows = grid.getStore().getRange();

How can we iterate over a grid?

for (var i = 0; i < yourGrid.getStore().data.length; i++) {
    var element = Ext.get(yourGrid.getView().getRow(i));
    var record = yourGrid.getStore().getAt(i);
    alert(record.data.ID);
}

How can we add tooltip to a grid cell?

Ext.define('huo.view.DocumentListGridPanel', {
    extend: 'Ext.grid.Panel',     
    alias : 'widget.documentListGridPanel',     

    [...]
    <!-- layout: 'fit',-->
    height: '100%',

    columns: [
    [...]
    {
        text     : 'Annotation',
        flex:1,
        sortable : true,
        dataIndex: 'annotation',
        renderer : function(value, metadata, record) {
          if(record.get('annotation') === ''){
             return record.get('annotation');
          }else{
             myToolTipText = "<b>Annotation</b>";
             myToolTipText = myToolTipText + "<br/>"+ record.get('annotation');
             metadata.tdAttr = 'data-qtip="' + myToolTipText + '"';
             return value;
          }
        }
    }
    ],
    viewConfig: {
    }
});

For Ext 3, use:

metaData.attr = 'ext:qtip="Not yet known.  Waiting for the back-end batch job to pick up the Excel file."';

The above code use a renderer function.

Event handling:

{
    xtype:'editorgrid',
    id: 'corpfacgrd',
    singleClick: true,
    height:300,
    border: true,
    trackMouseOver: false,
    stripeRows : false,
    store: qsupport.productactivation.corporateStore,
    columns: childFacColDefs,
    enableColumnMove: false,
    enableColumnHide:false,
    disableSelection: true,
    clicksToEdit: 1,
    view: new Ext.grid.GridView({
        scrollOffset: 10,
        deferEmptyText:false,
        emptyText: 'No Records Exists.',
        forceFit: true
    }),
    listeners: {
        'beforeedit': qsupport.productactivation.corporateScenario.corporateGrid_onbeforeedit,
        'cellclick': qsupport.productactivation.corporateScenario.corporateGrid_oncellclick
    }
}

qsupport.productactivation.corporateScenario.corporateGrid_onbeforeedit = function (e) {
    var projectsid = qsupport.productactivation.projectsid;
    if (qsupport.productactivation.isAbbreviationRequired()) {
        var validCheck = Ext.getCmp('corpAbbreviation').isValid();
        if(validCheck == false || buttonDisableFlag == false){
            Ext.Msg.show({
                title: 'Warning',
                icon: Ext.MessageBox.WARNING,
                minWidth: 300,
                msg: 'Enter Abbreviation for Corporate and Save',
                buttons: Ext.Msg.OK                    
            });
            return false;
        }
    }
    if (projectsid == 802) {
        if(e.record.get('patientReadOnly') == 'C' && e.record.get('VisitorReadOnly') == 'C' && e.record.get('EmployeeReadOnly') == 'C'){
            Ext.Msg.show({
                title: 'Warning',
                icon: Ext.MessageBox.WARNING,
                minWidth: 300,
                msg: 'All Modules Released for this Facility',
                buttons: Ext.Msg.OK                                        
            });    
            return false;
        }
    }
    if (projectsid == 2000) {
        if (e.record.get('STATUS') == 'A') {
            return false;
        }
    }
    if (projectsid == 3000) {
        if (e.record.get('STATUS') == 'A') {
            return false;
        }
    }
};

qsupport.productactivation.corporateScenario.corporateGrid_oncellclick = function (grid,rindex,cindex,e) {
    //alert(grid.getStore().getAt(rindex).data.FISCAL_YEAR_START+','+grid.getStore().getAt(rindex).data.ADDDATE);
    if(cindex == 2){
        if(qsupport.productactivation.projectsid  == 900) {//IRIS            
            var record = qsupport.productactivation.corporateStore.getAt(rindex);
            var colModel = Ext.getCmp('corpfacgrd').getColumnModel();
            if(record.get('STATUS') == 'A'){
                colModel.setEditable(2, false);
                colModel.setEditable(3, false);
            }
            else {
                colModel.setEditable(2, true);
                colModel.setEditable(3, true);
            }
            /*if(record.isModified('FISCAL_YEAR_START')){
                colModel.setEditable(2, true);
            }*/

        } else if(qsupport.productactivation.projectsid  == 3000){ //MMSEA
            var record = qsupport.productactivation.corporateStore.getAt(rindex);
            var colModel = Ext.getCmp('corpfacgrd').getColumnModel();
            if (record.get('STATUS') == 'A') {
                colModel.setEditable(2, false);
                colModel.setEditable(3, false);
            } else {
                colModel.setEditable(2, true);
                colModel.setEditable(3, true);
            }
        } else if (qsupport.productactivation.projectsid  == 2000) {
            // For PSOM, this is the dropdown of selectable PSOs
            var record = qsupport.productactivation.corporateStore.getAt(rindex);
            var colModel = Ext.getCmp('corpfacgrd').getColumnModel();
            if (record.get('STATUS') == 'A') {
                colModel.setEditable(2, false);
            } else {
                colModel.setEditable(2, true);
            }
        } else if(qsupport.productactivation.isAbbreviationRequired()) { 
            if(qsupport.productactivation.isChildFacilityActivation()) {
                //if(qsupport.productactivation.projectsid  == 804 ||qsupport.productactivation.projectsid==955 ||qsupport.productactivation.projectsid==807 || qsupport.productactivation.projectsid==802 ){
                var colModel = Ext.getCmp('corpfacgrd').getColumnModel();
                var record = qsupport.productactivation.corporateStore.getAt(rindex);
                var check= record.get('FACILITY_ABBREVIATION');
                if(check == null || check == '' || check == 'null'){
                    colModel.setEditable(2, true);
                    //Ext.getCmp('corpfacgrd').startEditing(parseInt(rindex),2);
                }
                else {
                    colModel.setEditable(2, false);
                    return;
                }
            }
        } else {
            var record = qsupport.productactivation.corporateStore.getAt(rindex);
            var colModel = Ext.getCmp('corpfacgrd').getColumnModel();
            if(record.get('FACILITY_ABBREVIATION') != ''){
                colModel.setEditable(2, false);
            }
            else{
                colModel.setEditable(2, true);
            }
            if(record.isModified('FACILITY_ABBREVIATION')){
                colModel.setEditable(2, true);
            }        
        }
    }
    if(cindex == 4){
        var record = qsupport.productactivation.corporateStore.getAt(rindex);
        //alert(record.get('patientReadOnly'));
        //alert(record.get('PatientStatus'));
        //alert(record.get('PATIENT_TYPE'));
        if(record.get('patientReadOnly') == 'C' && record.get('PatientStatus') == 'Y' && record.get('PATIENT_TYPE')!= '') {
            var careSettingCombo = Ext.getCmp('corpfacgrd').getColumnModel();
            careSettingCombo.setEditable(4, false);
        } else if(record.get('PatientStatus') == 'Y') {
            var careSettingCombo = Ext.getCmp('corpfacgrd').getColumnModel();
            careSettingCombo.setEditable(4, true);
        } else if(record.get('patientReadOnly') == 'I' && record.get('PatientStatus') == 'Y') {
            var careSettingCombo = Ext.getCmp('corpfacgrd').getColumnModel();
            careSettingCombo.setEditable(4, true);
        }
    }
};

How can we deselect all records?

childFacGrid.getSelectionModel().deselectAll();

How can we select all records?

childFacGrid.getSelectionModel().selectAll();

How can we select a particular record?

If we have a particular record, we can select it:

childFacGrid.getSelectionModel().select(record, keepExisting, suppressEvent);
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License