1. #1
    Ext JS Premium Member
    Join Date
    Aug 2007
    Location
    Antwerp, Belgium
    Posts
    559
    Vote Rating
    29
    joeri has a spectacular aura about joeri has a spectacular aura about joeri has a spectacular aura about

      0  

    Default Ext.ux.grid.RowSelectionPaging

    Ext.ux.grid.RowSelectionPaging


    An annoying problem with paging grids is that they don't remember their selections across paging. This plugin fixes that by preserving selections across paging events and store reloads. It also provides methods to fetch and clear the entire selection (across all pages).

    Licensed under http://www.gnu.org/licenses/lgpl-3.0.txt

    Ext.ux.grid.RowSelectionPaging.js:
    Code:
    /**
     * Ext.ux.grid.RowSelectionPaging plugin for Ext.grid.GridPanel
     * A grid plugin that preserves row selections across paging / filtering of the store.
     *
     * @author  Joeri Sebrechts
     * @date    October 21st, 2009
     *
     * @class Ext.ux.grid.RowSelectionPaging
     * @extends Ext.util.Observable
     */
    Ext.ns('Ext.ux.grid'); 
    Ext.ux.grid.RowSelectionPaging = function(config) {
        Ext.apply(this, config);
    };
    Ext.extend(Ext.ux.grid.RowSelectionPaging, Ext.util.Observable, {
        init: function(grid) {
           this.grid = grid;
           this.selections = []; // array of selected records
           this.selected = {}; // hash mapping record id to selected state
           grid.on('render', function() {
             // attach an interceptor for the selModel's onRefresh handler
             this.grid.view.un('refresh', this.grid.selModel.onRefresh, this.grid.selModel);
             this.grid.view.on('refresh', this.onViewRefresh, this );
             // add a handler to detect when the user changes the selection
             this.grid.selModel.on('rowselect', this.onRowSelect, this );
             this.grid.selModel.on('rowdeselect', this.onRowDeselect, this);
             // patch selModel to detect selection cleared events
             var scope = this;
             this.selModelClearSelections = this.grid.selModel.clearSelections;
             this.grid.selModel.clearSelections = function(fast) {
                scope.selModelClearSelections.call(this, fast);
                scope.onSelectionClear();
             };
             // and replace the default behavior of the "check all"
             if (!this.originalSelectAll && (this.grid.selModel.id == 'checker')) {
                this.grid.selModel.onHdMouseDown = function(e, t) {
                   if(t.className == 'x-grid3-hd-checker'){
                       e.stopEvent();
                       var hd = Ext.fly(t.parentNode);
                       var isChecked = hd.hasClass('x-grid3-hd-checker-on');
                       if(isChecked){
                           hd.removeClass('x-grid3-hd-checker-on');
                           scope.clearSelections();
                       }else{
                           hd.addClass('x-grid3-hd-checker-on');
                           scope.selectAll();
                       }
                   }
                }
             }
           }, this);
        }, // end init
        
        // private
        onViewRefresh: function() {
           this.ignoreSelectionChanges = true;
           // explicitly refresh the selection model
           this.grid.selModel.onRefresh();
           // selection changed from view updates, restore full selection
           var ds = this.grid.getStore();
           var newSel = [];
           for (var i = ds.getCount() - 1; i >= 0; i--) {
              if (this.selected[ds.getAt(i).id]) {
                 newSel.push(i);
              }
           }
           this.grid.selModel.selectRows(newSel, false);
           this.ignoreSelectionChanges = false;
        }, // end onViewRefresh
        
        // private
        onSelectionClear: function() {
           if (! this.ignoreSelectionChanges) {
              // selection cleared by user
              // also called internally when the selection replaces the old selection
              this.selections = [];
              this.selected = {};
           }
        }, // end onSelectionClear
        
        // private
        onRowSelect: function(sm, i, rec) {
           if (! this.ignoreSelectionChanges) {
              if (!this.selected[rec.id]) 
              {
                 this.selections.push(rec);
                 this.selected[rec.id] = true;
              }
           }
        }, // end onRowSelect
        
        // private
        onRowDeselect: function(sm, i, rec) {
           if (!this.ignoreSelectionChanges) {
              if (this.selected[rec.id]) {
                 for (var i = this.selections.length - 1; i >= 0; i--) {
                    if (this.selections[i].id == rec.id) {
                       this.selections.splice(i, 1);
                       this.selected[rec.id] = false;
                       break;
                    }
                 }
              }
           }
        }, // end onRowDeselect
        
        /**
         * Clears selections across all pages
         */
        clearSelections: function() {
           this.selections = [];
           this.selected = {};
           this.onViewRefresh();
        }, // end clearSelections
        
        /**
         * Returns the selected records for all pages
         * @return {Array} Array of selected records
         */
        getSelections: function() {
           return [].concat(this.selections);
        }, // end getSelections
        
        /**
         * Selects all the rows in the grid, including those on other pages
         * Be very careful using this on very large datasets
         */
        selectAll: function() {
           var ds = this.grid.getStore();
           ds.suspendEvents();
           ds.load({ 
              params: {start: 0, limit: ds.getTotalCount() },
              callback: function() {
                 this.selections = ds.data.items.slice(0);
                 this.selected = {};
                 for (var i = this.selections.length - 1; i >= 0; i--) {
                    this.selected[this.selections[i].id] = true;
                 };
                 ds.resumeEvents();
                 this.onViewRefresh();
              },
              scope: this
           });
        }
    });
    To use, just load it as a plugin:
    Code:
    var pagingSelection = new Ext.ux.grid.RowSelectionPaging();
    ...
      plugins: [pagingSelection],
    ...
    A demo can be found at http://sebrechts.net/demo/rowselectionpaging/

    Update (2009-10-21):
    I've added support for a true select all operation (via the checkbox selection model). To get back to original "select all" behavior, create the plugin with "new Ext.ux.grid.RowSelectionPaging({originalSelectAll: true})"
    Update (2009-10-22):
    Btw, yesterday's update was tested on 2.2 and 3.0, so it should be compatible with both releases.
    Last edited by joeri; 22 Oct 2009 at 1:39 AM. Reason: note on version compatibility

  2. #2
    Ext User zhw511006's Avatar
    Join Date
    Oct 2008
    Posts
    39
    Vote Rating
    0
    zhw511006 is on a distinguished road

      0  

    Default


    maybe a live demo!

    thank you!

  3. #3
    Ext JS Premium Member
    Join Date
    Aug 2007
    Location
    Antwerp, Belgium
    Posts
    559
    Vote Rating
    29
    joeri has a spectacular aura about joeri has a spectacular aura about joeri has a spectacular aura about

      0  

    Default


    I've placed the sample code from the first post online at

    http://sebrechts.net/demo/rowselectionpaging/

    Just make selections with the checkboxes on different pages and navigate back and forth. You'll see what it does.

  4. #4
    Ext JS Premium Member
    Join Date
    Mar 2007
    Location
    NL
    Posts
    608
    Vote Rating
    1
    mdissel is on a distinguished road

      0  

    Default


    Nice! one little error, you're paging on the server is wrong, it's starting from the last record of the previous page..

    As i understand it right, clicking on the 'select all' checkbox will select the current page, but 'deselect' all will remove all selections from all pages... one enhancement could be add ask the user what to do, select all in alle page or only current page, the same for deselecting..

    Thanks
    Marco

  5. #5
    Ext JS Premium Member
    Join Date
    Aug 2007
    Location
    Antwerp, Belgium
    Posts
    559
    Vote Rating
    29
    joeri has a spectacular aura about joeri has a spectacular aura about joeri has a spectacular aura about

      0  

    Default


    Nice catch, I was copy/pasting out of some test code I made without realizing the paging was buggy. I've fixed the demo code in the first post.

    The reason for the deselect all deselecting across all pages is because the plugin overrides the behavior of the selModel's clearSelections method. If you take that part out of the plugin code, it still works just fine, but it exhibits more consistent behavior from the perspective of the "select all" checkbox.

    Having it the other way around, with select all selecting across all pages, means performing a separate request to the server to fetch all rows in the dataset. I'm not sure how desirable that behavior would be.

    Besides, the select all checkbox is buggy anyway. If you check it, and navigate to the next page, it's still checked.

  6. #6
    Sencha User galdaka's Avatar
    Join Date
    Mar 2007
    Location
    Spain
    Posts
    1,166
    Vote Rating
    -1
    galdaka is an unknown quantity at this point

      0  

    Default


    Hi,

    Excellent work!!

    Would be interesting return the selected IDs.

    Greetings,

  7. #7
    Ext JS Premium Member
    Join Date
    Aug 2007
    Location
    Antwerp, Belgium
    Posts
    559
    Vote Rating
    29
    joeri has a spectacular aura about joeri has a spectacular aura about joeri has a spectacular aura about

      0  

    Default


    The plugin's getSelections method returns the complete array of selected records, so it would be straightforward to fetch the id's from that.

  8. #8
    Sencha User
    Join Date
    Jun 2009
    Posts
    49
    Vote Rating
    0
    thiner is on a distinguished road

      0  

    Default it's a awesome extension!

    it's a awesome extension!


    I see the Demo on your site, it's awesome!
    But I got problem when I try to use it in this Example:http://www.extjs.com/deploy/dev/exam...id/paging.html

    It seems like the selected rows' data have been saved into the 'selected' variable, but the 'checked' check box which made in previous action became 'unchecked' when I page-back from other pages.
    Can anyone please tell me why?
    Thanks a lot.

  9. #9
    Ext JS Premium Member
    Join Date
    Aug 2007
    Location
    Antwerp, Belgium
    Posts
    559
    Vote Rating
    29
    joeri has a spectacular aura about joeri has a spectacular aura about joeri has a spectacular aura about

      0  

    Default


    thiner, that example doesn't seem to use selection. Could you post an example of the problem you mentioned?

  10. #10
    Ext User
    Join Date
    Mar 2007
    Posts
    7
    Vote Rating
    0
    chaos is on a distinguished road

      0  

    Default


    Very useful plugins!
    Actually I don't need it but i would suggest to improve the <shift> key management to work across different pages.
    Example:
    select one or more records on a page, go in another page and select other records with the <shift> key. Actually it clear the selection in the first page (I'm not saying this is wrong), but it could be usefull to limit the logic of <shift> to the single page.
    It could be an option in the config object.

    ciao
    -Mario