View Full Version : [FIXED-95][3.0.0] locked selectionmodel selections not preserved on sort

28 Jul 2009, 1:01 AM
Ext version tested:
Ext 3.0.0
Adapter used:
css used:
only default ext-all.css
Browser versions tested against:
IE8 FF3.5 (firebug 1.4.0_b7 installed)
Operating System:
WinXP Pro

If one locks the selected rows in a grid and sorts the records of the grid by any column of the grid, the selected rows are no longer selected afterwards.
If the grid is not locked, the selected rows are kept after sorting.
The selection should also be kept in case of locking the SelectionModel of the grid.

Test Case:
The test case ist based on the array grid example of ext 3.0.0. It adds a button to the bottom-toolbar of the grid to lock or unlock the current selection.

File: BugLockedGrid.js

* Ext JS Library 3.0.0
* Copyright(c) 2006-2009 Ext JS, LLC
* [email protected]
* http://www.extjs.com/license

// NOTE: This is an example showing simple state management. During development,
// it is generally best to disable state management as dynamically-generated ids
// can change across page loads, leading to unpredictable results. The developer
// should ensure that stable state ids are set for stateful components in real apps.
Ext.state.Manager.setProvider(new Ext.state.CookieProvider());

var myData = [
['3m Co',71.72,0.02,0.03,'9/1 12:00am'],
['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
['Altria Group Inc',83.81,0.28,0.34,'9/1 12:00am'],
['American Express Company',52.55,0.01,0.02,'9/1 12:00am'],
['American International Group, Inc.',64.13,0.31,0.49,'9/1 12:00am'],
['AT&T Inc.',31.61,-0.48,-1.54,'9/1 12:00am'],
['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
['Caterpillar Inc.',67.27,0.92,1.39,'9/1 12:00am'],
['Citigroup, Inc.',49.37,0.02,0.04,'9/1 12:00am'],
['E.I. du Pont de Nemours and Company',40.48,0.51,1.28,'9/1 12:00am'],
['Exxon Mobil Corp',68.1,-0.43,-0.64,'9/1 12:00am'],
['General Electric Company',34.14,-0.08,-0.23,'9/1 12:00am'],
['General Motors Corporation',30.27,1.09,3.74,'9/1 12:00am'],
['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
['Honeywell Intl Inc',38.77,0.05,0.13,'9/1 12:00am'],
['Intel Corporation',19.88,0.31,1.58,'9/1 12:00am'],
['International Business Machines',81.41,0.44,0.54,'9/1 12:00am'],
['Johnson & Johnson',64.72,0.06,0.09,'9/1 12:00am'],
['JP Morgan & Chase & Co',45.73,0.07,0.15,'9/1 12:00am'],
['McDonald\'s Corporation',36.76,0.86,2.40,'9/1 12:00am'],
['Merck & Co., Inc.',40.96,0.41,1.01,'9/1 12:00am'],
['Microsoft Corporation',25.84,0.14,0.54,'9/1 12:00am'],
['Pfizer Inc',27.96,0.4,1.45,'9/1 12:00am'],
['The Coca-Cola Company',45.07,0.26,0.58,'9/1 12:00am'],
['The Home Depot, Inc.',34.64,0.35,1.02,'9/1 12:00am'],
['The Procter & Gamble Company',61.91,0.01,0.02,'9/1 12:00am'],
['United Technologies Corporation',63.26,0.55,0.88,'9/1 12:00am'],
['Verizon Communications',35.57,0.39,1.11,'9/1 12:00am'],
['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am']

// example of custom renderer function
function change(val){
if(val > 0){
return '<span style="color:green;">' + val + '</span>';
}else if(val < 0){
return '<span style="color:red;">' + val + '</span>';
return val;

// example of custom renderer function
function pctChange(val){
if(val > 0){
return '<span style="color:green;">' + val + '%</span>';
}else if(val < 0){
return '<span style="color:red;">' + val + '%</span>';
return val;

// create the data store
var store = 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'}

// create the Grid
var grid = new Ext.grid.GridPanel({
id: 'lockableGrid',
store: store,
columns: [
{id:'company',header: "Company", width: 160, sortable: true, dataIndex: 'company'},
{header: "Price", width: 75, sortable: true, renderer: 'usMoney', dataIndex: 'price'},
{header: "Change", width: 75, sortable: true, renderer: change, dataIndex: 'change'},
{header: "% Change", width: 75, sortable: true, renderer: pctChange, dataIndex: 'pctChange'},
{header: "Last Updated", width: 85, sortable: true, renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
stripeRows: true,
autoExpandColumn: 'company',
title:'Array Grid',
bbar: [
{xtype: 'button'
,text: 'Lock Selection'
,handler: function(button, event) {
var smLockableGrid=Ext.getCmp('lockableGrid').getSelectionModel();
if (smLockableGrid.isLocked()) {
button.setText('Lock Selection');
else {
button.setText('Unlock Selection');
} //eo button


File: BugLockedGrid.html

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Array Grid Example</title>
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
<link rel="stylesheet" type="text/css" href="grid-examples.css" />

<!-- Common Styles for the examples -->
<link rel="stylesheet" type="text/css" href="../shared/examples.css" />
<!-- GC -->
<!-- LIBS -->
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<!-- ENDLIBS -->

<script type="text/javascript" src="../../ext-all.js"></script>
<script type="text/javascript" src="BugLockedGrid.js"></script>

<script type="text/javascript" src="../shared/examples.js"></script><!-- EXAMPLES -->
<h1>Array Grid Example</h1>
<p>This example shows how to create a grid from Array data.</p>
<p>Note that the js is not minified so it is readable. See <a href="array-grid.js">array-grid.js</a>.</p>

<div id="grid-example"></div>

Steps to reproduce the problem:

Select one or more rows in the grid
Lock the SelectionModel by clicking on the Button 'Lock Selection'
Sort the grid by any of the columns

The result that was expected:

The selected rows should be kept after sorting by any of the columns

The result that occurs instead:

The selected rows are no longer selected after sorting.

Debugging already done:
Possible fix:
not provided

28 Jul 2009, 4:55 AM
Thanks for the complete report.

29 Jul 2009, 12:49 AM
I did some debugging with the testcase this morning and I think I was able to locate the problem:
If the grid is sorted by clicking on a sortable column, a refresh for the grid occurs. The selected rows are no longer selected and are restored by the method onRefresh in RowSelectionModel.js (r.129ff):

// private
onRefresh : function(){
var ds = this.grid.store, index;
var s = this.getSelections();
for(var i = 0, len = s.length; i < len; i++){
var r = s[i];
if((index = ds.indexOfId(r.id)) != -1){
this.selectRow(index, true);
if(s.length != this.selections.getCount()){
this.fireEvent("selectionchange", this);

This works well as long as the selection is not locked. If the selection is locked, then the method 'selectRow' fails to restore the selected row because it returns to early when the if-clause this.isLocked() is true.

RowSelectionModel.js, r.421ff

* Selects a row. Before selecting a row, checks if the selection model
* {@link Ext.grid.AbstractSelectionModel#isLocked is locked} and fires the
* {@link #beforerowselect} event. If these checks are satisfied the row
* will be selected and followed up by firing the {@link #rowselect} and
* {@link #selectionchange} events.
* @param {Number} row The index of the row to select
* @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections
* @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to
* prevent notifying the view (disables updating the selected appearance)
selectRow : function(index, keepExisting, preventViewNotify){
if(this.isLocked() || (index < 0 || index >= this.grid.store.getCount()) || (keepExisting && this.isSelected(index))){
var r = this.grid.store.getAt(index);
if(r && this.fireEvent("beforerowselect", this, index, keepExisting, r) !== false){
if(!keepExisting || this.singleSelect){
this.last = this.lastActive = index;
this.fireEvent("rowselect", this, index, r);
this.fireEvent("selectionchange", this);

A possible workaround for this problem could try to unlock the selectionModel when a sort of the table occurs and afterwards lock it again.
However, I am not sure which implications this would bring in and where to place to necessary code. :-?
It would be nice if someone could point me to the right method to test out this solution.

Jamie Avins
4 Mar 2010, 3:59 PM
[type]: fix
[module]: SelectionModel
[id]: #95
[desc]: Fixes #95. SelectionModel will now add/remove events to temporarily allow reselection while a refresh is in progress.