Persisting checkboxes in grid

Have you ever used checkboxes to select multiple rows in grid and was disappointed that when you use pagination or filters, grid doesn't remember yours choices?

Preparation

For the beginning we will create simple grid, with two columns (id and name).

BXR.grid.Items = function(config) {
    config = config || {};
    Ext.applyIf(config,{
        id: 'bxr-grid-items'
        ,url: BXR.config.connectorUrl
        ,baseParams: {
            action: 'mgr/items/getlist'
        }
        ,paging: true
        ,autoExpandColumn: 'name'
        ,fields: ['id' ,'name']
        ,columns: [{
            header: _('id')
            ,dataIndex: 'id'
        },{
            header: _('name')
            ,dataIndex: 'name'
        }]
    });
    BXR.grid.Items.superclass.constructor.call(this,config);
};
Ext.extend(BXR.grid.Items,MODx.grid.Grid, {});
Ext.reg('bxr-grid-items',BXR.grid.Items);

Creting the selection model

We have the basic grid and now we will want to add checboxes before id column. At first we will need to create checkbox selection model that you will inject to grid itself and grid columns. So add this line right after config = config || {};.

this.sm = new Ext.grid.CheckboxSelectionModel();

Next we will need to set grid's selection model to ours created and add column with checboxes. To set grid's selection model add sm: this.sm to grid's config and for adding checkboxes add this.sm as your first column. You should get

BXR.grid.Items = function(config) {
    config = config || {};
    this.sm = new Ext.grid.CheckboxSelectionModel();
    Ext.applyIf(config,{
        id: 'bxr-grid-items'
        ,url: BXR.config.connectorUrl
        ,baseParams: {
            action: 'mgr/items/getlist'
        }
        ,paging: true
        ,sm: this.sm
        ,autoExpandColumn: 'name'
        ,fields: ['id' ,'name']
        ,columns: [this.sm,{
            header: _('id')
            ,dataIndex: 'id'
        },{
            header: _('name')
            ,dataIndex: 'name'
        }]
    });
    BXR.grid.Items.superclass.constructor.call(this,config);
};
Ext.extend(BXR.grid.Items,MODx.grid.Grid, {});
Ext.reg('bxr-grid-items',BXR.grid.Items);

Now when you reload the page with your grid you should see checkboxes as a first column in your grid. But when you select something and use pagination, the grid won't remember your selections.

Let's teach grid something new

Now we want to persist our selection, so we will be able to use pagination (filters, etc.) without loosing our selected rows.

At first we will need to extend our selection model with listeners for selecting and deselecting row. Replace this.sm = new Ext.grid.CheckboxSelectionModel(); with

    this.sm = new Ext.grid.CheckboxSelectionModel({
        listeners: {
            rowselect: function(sm, rowIndex, record) {
                this.rememberRow(record);
            }, scope: this
            ,rowdeselect: function(sm, rowIndex, record) {
                this.forgotRow(record);
            }, scope: this
        }
    }

This will call rememberRow method when a row is selected and forgotRow method when the row is deselected. As parameter to both methods is passed object that contains row data.

Now we will need to create this two methods and add some variable that will store our selected rows. So lets do it and replace Ext.extend(BXR.grid.Items,MODx.grid.Grid, {}); with

Ext.extend(BXR.grid.Items,MODx.grid.Grid, {
    selectedRecords: []
    ,rememberRow: function(record) {
        if(this.selectedRecords.indexOf(record.id) == -1){
            this.selectedRecords.push(record.id);
        }
    }
    ,forgotRow: function(record){
        this.selectedRecords.remove(record.id);
    }
});

This will basicly add id of selected row to the selectedRecords array or remove the id from the array.

Now the grid will remember all yours selections, but when you try it, you won't see them. We need to add method that will select rows based on what is in selectedRecords array on view's refresh (that's called when you paginate, filter, refresh store,....). Lets add refreshSelection method right after forgotRow method.

    ,refreshSelection: function() {
        var rowsToSelect = [];
        Ext.each(this.selectedRecords, function(item){
            rowsToSelect.push(this.store.indexOfId(item));
        },this);
        this.getSelectionModel().selectRows(rowsToSelect);
    }

And now we have to connect this method with refresh event, so add this line right after BXR.grid.Items.superclass.constructor.call(this,config);

this.getView().on('refresh', this.refreshSelection, this);

And as the last move, we will override getSelectedAsList method to return selected rows from our array. Simply add theese lines after refreshSelection method

    ,getSelectedAsList: function(){
        return this.selectedRecords.join();
    }

That's it!

Everything together

Here is the whole grid

BXR.grid.Items = function(config) {
    config = config || {};
    this.sm = new Ext.grid.CheckboxSelectionModel({
        listeners: {
            rowselect: function(sm, rowIndex, record) {
                this.rememberRow(record);
            }, scope: this
            ,rowdeselect: function(sm, rowIndex, record) {
                this.forgotRow(record);
            }, scope: this
        }
    });
    Ext.applyIf(config,{
        id: 'bxr-grid-items'
        ,url: BXR.config.connectorUrl
        ,baseParams: {
            action: 'mgr/items/getlist'
        }
        ,paging: true
        ,autoExpandColumn: 'name'
        ,fields: ['id' ,'name']
        ,sm: this.sm
        ,columns: [this.sm,{
            header: _('id')
            ,dataIndex: 'id'
        },{
            header: _('name')
            ,dataIndex: 'name'
        }]
    });
    BXR.grid.Items.superclass.constructor.call(this,config);
    this.getView().on('refresh', this.refreshSelection, this);
};
Ext.extend(BXR.grid.Items,MODx.grid.Grid, {
    selectedRecords: []
    ,rememberRow: function(record) {
        if(this.selectedRecords.indexOf(record.id) == -1){
            this.selectedRecords.push(record.id);
        }
    }
    ,forgotRow: function(record){
        this.selectedRecords.remove(record.id);
    }
    ,refreshSelection: function() {
        var rowsToSelect = [];
        Ext.each(this.selectedRecords, function(item){
            rowsToSelect.push(this.store.indexOfId(item));
        },this);
        this.getSelectionModel().selectRows(rowsToSelect);
    }
    ,getSelectedAsList: function(){
        return this.selectedRecords.join();
    }
});
Ext.reg('bxr-grid-items',BXR.grid.Items);
Post By John Peca ExtJS, MODX