-
20 Dec 2011 12:32 PM #1
Ext.ux.grid.plugin.PagingSelectionPersistence
Ext.ux.grid.plugin.PagingSelectionPersistence
This plugin is loosely based on Ext.ux.grid.RowSelectionPaging, a 3.x plugin by Joeri Sebrechts, and is used to maintain the selection state of rows in a paginated Grid Panel when moving between pages. This is tested in Ext 4.0.7, however I have so far only used it with Ext.selection.CheckboxModel, and it should work with RowModel, but if anyone runs into any issues with it definitely let me know.
Demo: http://jsfiddle.net/Whkbu/2/
IMPORTANT!
Currently this plugin will only work if loadMask is set to false in the grid's viewConfig. I have been digging through Ext.LoadMask's code to try to find the source of this issue, but no luck yet. If anyone identifies the bug before I can, by all means post your fix here.
Simply enabling this plugin in your grid panel will allow it to maintain the selection state of the rows between pages visually, however using the selection model's getSelection() method will still retain its default behavior and only return the array of selected records for the current page. In order to get the array of ALL selected records across all pages, you must use the plugin's getPersistedSelection() method, like this:
Likewise, to clear selected records across all pages, use the plugin's clearPersistedSelection() method.Code:var gridPanel = Ext.create('Ext.grid.Panel', { ... viewConfig: { loadMask : false //this setting is currently required for the plugin to work (see above) }, plugins : [ { ptype : 'pagingselectpersist' } ] }); var selection = gridPanel.getPlugin('pagingSelectionPersistence'). getPersistedSelection(); //=> Ext.data.Model[]
Also, it should be important to note that the default behavior of the CheckboxModel's header checkbox has been left intact, in that checking it will only select all rows on the current page, and unchecking it will only deselect all rows on the current page. That was just my personal preference, as I feel that the user may be confused as to why records on other pages are selected or deselected when clicking that header checkbox (not to mention, selecting all records across all pages could easily lead to scaling issues for grids with very large datasets). However, I'm sure this could easily be extended by someone else to provided that functionality if it is desired.
Code:/** * Grid PagingSelectionPersistence plugin * * Maintains row selection state when moving between pages of a paginated grid * * Public Methods: * getPersistedSelection() - retrieve the array of selected records across all pages * clearPersistedSelection() - deselect records across all pages * * * @class Ext.ux.grid.plugin.PagingSelectionPersistence * @extends Ext.AbstractPlugin * @author Bill Dami * @date December 20th, 2011 */ Ext.define('Ext.ux.grid.plugin.PagingSelectionPersistence', { alias: 'plugin.pagingselectpersist', extend: 'Ext.AbstractPlugin', pluginId: 'pagingSelectionPersistence', //array of selected records selection: [], //hash map of record id to selected state selected: {}, init: function(grid) { this.grid = grid; this.selModel = this.grid.getSelectionModel(); this.isCheckboxModel = (this.selModel.$className == 'Ext.selection.CheckboxModel'); this.origOnHeaderClick = this.selModel.onHeaderClick; this.bindListeners(); }, destroy: function() { this.selection = []; this.selected = {}; this.disable(); }, enable: function() { var me = this; if(this.disabled && this.grid) { this.grid.getView().on('refresh', this.onViewRefresh, this); this.selModel.on('select', this.onRowSelect, this); this.selModel.on('deselect', this.onRowDeselect, this); if(this.isCheckboxModel) { //For CheckboxModel, we need to detect when the header deselect/select page checkbox //is clicked, to make sure the plugin's selection array is updated. This is because Ext.selection.CheckboxModel //interally supresses event firings for selectAll/deselectAll when its clicked this.selModel.onHeaderClick = function(headerCt, header, e) { var isChecked = header.el.hasCls(Ext.baseCSSPrefix + 'grid-hd-checker-on'); me.origOnHeaderClick.apply(this, arguments); if(isChecked) { me.onDeselectPage(); } else { me.onSelectPage(); } }; } } this.callParent(); }, disable: function() { if(this.grid) { this.grid.getView().un('refresh', this.onViewRefresh, this); this.selModel.un('select', this.onRowSelect, this); this.selModel.un('deselect', this.onRowDeselect, this); this.selModel.onHeaderClick = this.origOnHeaderClick; } this.callParent(); }, bindListeners: function() { var disabled = this.disabled; this.disable(); if(!disabled) { this.enable(); } }, onViewRefresh : function(view, eOpts) { var store = this.grid.getStore(), sel = [], hdSelectState, rec, i; this.ignoreChanges = true; for(i = store.getCount() - 1; i >= 0; i--) { rec = store.getAt(i); if(this.selected[rec.getId()]) { sel.push(rec); } } this.selModel.select(sel, false); if(this.isCheckboxModel) { //For CheckboxModel, make sure the header checkbox is correctly //checked/unchecked when the view is refreshed depending on the //selection state of the rows on that page (workaround for possible bug in Ext 4.0.7?) hdSelectState = (this.selModel.selected.getCount() === this.grid.getStore().getCount()); this.selModel.toggleUiHeader(hdSelectState); } this.ignoreChanges = false; }, onRowSelect: function(sm, rec, idx, eOpts) { if(this.ignoreChanges === true) { return; } if(!this.selected[rec.getId()]) { this.selection.push(rec); this.selected[rec.getId()] = true; } }, onRowDeselect: function(sm, rec, idx, eOpts) { var i; if(this.ignoreChanges === true) { return; } if(this.selected[rec.getId()]) { for(i = this.selection.length - 1; i >= 0; i--) { if(this.selection[i].getId() == rec.getId()) { this.selection.splice(i, 1); this.selected[rec.getId()] = false; break; } } } }, onSelectPage: function() { var sel = this.selModel.getSelection(), len = this.getPersistedSelection().length, i; for(i = 0; i < sel.length; i++) { this.onRowSelect(this.selModel, sel[i]); } if(len !== this.getPersistedSelection().length) { this.selModel.fireEvent('selectionchange', this.selModel, [], {}); } }, onDeselectPage: function() { var store = this.grid.getStore(), len = this.getPersistedSelection().length, i; for(i = store.getCount() - 1; i >= 0; i--) { this.onRowDeselect(this.selModel, store.getAt(i)); } if(len !== this.getPersistedSelection().length) { this.selModel.fireEvent('selectionchange', this.selModel, [], {}); } }, getPersistedSelection: function() { return [].concat(this.selection); }, clearPersistedSelection: function() { var changed = (this.selection.length > 0); this.selection = []; this.selected = {}; this.onViewRefresh(); if(changed) { this.selModel.fireEvent('selectionchange', this.selModel, [], {}); } } });
-
20 Dec 2011 12:45 PM #2Sencha - Senior Forum Manager
- Join Date
- Mar 2007
- Location
- St. Louis, MO
- Posts
- 33,624
- Vote Rating
- 434
Was waiting for someone to do this. The problem I see is how does one know that there are selections in other pages?
Mitchell Simoens @SenchaMitch
Sencha Inc, Senior Forum Manager
________________
http://www.JSONPLint.com - Source to lint your JSONP!
Check out my GitHub, lots of nice things for Ext JS 4 and Sencha Touch 2
https://github.com/mitchellsimoens
Think my support is good? Get more personalized support via a support subscription. https://www.sencha.com/store/
Need more help with your app? Hire Sencha Services services@sencha.com
Want to learn Sencha Touch 2? Check out Sencha Touch in Action that is almost in print!
When posting code, please use BBCode's CODE tags.
-
20 Dec 2011 1:01 PM #3
In my own application I just bind a listener to the selection model's 'selectionchange' which updates UI elements outside of the grid panel with the current selected record count using getPersistedSelection().length. I left the plugin pretty bare bones so developers could implement their own ways of showing the user what/how many is selected in their applications.
That being said I could see the usefulness of integrating the display of this information directly into the grid panel itself. Maybe something similar to how GMail handles it in their list view, displaying a strip above the first row in the grid telling the user how many records are selected?
-
20 Dec 2011 1:04 PM #4Sencha - Senior Forum Manager
- Join Date
- Mar 2007
- Location
- St. Louis, MO
- Posts
- 33,624
- Vote Rating
- 434
Mitchell Simoens @SenchaMitch
Sencha Inc, Senior Forum Manager
________________
http://www.JSONPLint.com - Source to lint your JSONP!
Check out my GitHub, lots of nice things for Ext JS 4 and Sencha Touch 2
https://github.com/mitchellsimoens
Think my support is good? Get more personalized support via a support subscription. https://www.sencha.com/store/
Need more help with your app? Hire Sencha Services services@sencha.com
Want to learn Sencha Touch 2? Check out Sencha Touch in Action that is almost in print!
When posting code, please use BBCode's CODE tags.
-
21 Dec 2011 2:12 AM #5
-
3 Jan 2012 9:31 AM #6
Add setSelectionPersistence method and fix a selection bug
Add setSelectionPersistence method and fix a selection bug
This plugin is perfect for me, so I added some stuff to make it more perfect. I needed a way to set what rows were already selected.
I also ran across an error that happened when a select event was dispatched without any rows in the selection, so I made sure to only try to select rows when there were rows to select.
Attached is a patch.
EDIT: My "fix" for the error (select event being dispatched with an empty selection array) made clearPersistedSelection() stop working. New patch is below, as the forum won't let me edit attachments that I see.
Code:diff --git a/ext-ux/grid/plugin/PagingSelectionPersistence.js b/ext-ux/grid/plugin/PagingSelectionPersistence.js index 3e3c6ac..f55d0f6 100644 --- a/ext-ux/grid/plugin/PagingSelectionPersistence.js +++ b/ext-ux/grid/plugin/PagingSelectionPersistence.js @@ -6,7 +6,8 @@ * Public Methods: * getPersistedSelection() - retrieve the array of selected records across all pages * clearPersistedSelection() - deselect records across all pages - * + * setPersistedSelection() - Set an array of selected records across all pages + * * * @class Ext.ux.grid.plugin.PagingSelectionPersistence * @extends Ext.AbstractPlugin @@ -190,5 +193,17 @@ Ext.define('Ext.ux.grid.plugin.PagingSelectionPersistence', { if(changed) { this.selModel.fireEvent('selectionchange', this.selModel, [], {}); } + }, + + setPersistedSelection: function(selection) { + var i = 0; + if ( !Ext.isArray( selection ) ) { + selection = [selection]; + } + this.selection = selection; + for ( ; i < selection.length; i++ ) { + this.selected[ selection[i].getId() ] = selection[i]; + } + this.onViewRefresh(); } });Last edited by preaction; 3 Jan 2012 at 10:28 AM. Reason: remove conditional call to selModel.select()
-
13 Jan 2012 8:55 AM #7
I'm having an issue with the headerCheckbox not deselecing when paging or sorting.
My quesiton is here:
http://www.sencha.com/forum/showthre...stion&p=714398
Any suggestions would be great. Thanks.
-
13 Jan 2012 9:10 AM #8
@preaction - Nice catch on that error, and definitely makes sense to have a method to set what rows are already selected. I'm going to update my version and the original post with those changes shortly.
@billp - Can't think of anything that would be causing that off the top of my head but I will definitely look into it when I get a chance, just have been rather busy last few weeks and haven't had time to work on it at all. I'll post anything ill find in this thread, so just check back here.
-
13 Jan 2012 9:14 AM #9
-
13 Jan 2012 2:12 PM #10
To provide a little more info, it seems that this only happens when you sort only on that one column.
If you select different columns to sort by then the header Check box goes away.
So basically, if i sort on ID, then check all boxes, the header is checked. If i page over or sort in revers on the ID col, the check mark stays. BUT if i sort on a different col, the check box goes away.
In the test on jsfiddle, works as expected, no matter how many times you sort on the same col.
UPDATE (1/17/12):
So it's not that col only. when my results are returned they are already sorted on a Description Col. If i check the header box, they all select as expected. If i resort by that col, or any other, the checkbox stays, even though no boxes are selected at that point. If i resort again on any column the checked header goes away. So it seems it needs a secondary sort to reset the counter for the header.
I'm using 4.0.2a


Reply With Quote