Reputation: 4514
I'm using a paginated version of SlickGrid in conjunction with the CheckboxSelection-Plugin. Unfortunately, the row selection gets lost when one leaves the current grid page and isn't restored when that page is accessed again. This behaviour can also be reproduced with SlickGrid example #4. (click the little light-bulb-icon in the grid footer to turn on pagination)
I've informed SlickGrid's author about this, but he doesn't see this behaviour as a bug, as SlickGrid is more a grid framework than a just-add-data-solution.
Does anyone have an idea on how to implement reliable selection persistence in SlickGrid, that works for both paginated and non-paginated grids?
Upvotes: 3
Views: 5670
Reputation: 13194
I know this is an old question but I had to implement this and the proposed solution didn't cover all use case (for example, the proposed solution by @fbuchinger removes some selection if user goes to last page, without doing any selections, and comes back to first page).
To deal with this issue, I had to do a much more complex implementation and I even had to add a new DataView event onBeforePagingInfoChanged
and also modified the SlickGrid event onSelectedRowsChanged
to also return previous row selections (on top of current selections). These changes were made in the 6pac/SlickGrid fork and were released under version 2.4.18
The steps I used are the following when using Pagination, we can't just use the getSelectedRows()
since this will only return the selection in current page which is not enough, we need to keep a global array with data object IDs (not the row indexes since that will keep changing in a page), to keep both the grid indexes and the data IDs, we'll alternate between dataView.mapIdsToRows()
and dataView.mapRowsToIds()
.
The steps of the code shown below are the following
// global variables
_selectedRowDataContextIds = []; // used with row selection
_wasRecheckedAfterPageChange = true; // used with row selection & pagination
function bindSlickgridEventsForRowSelectionWithPagination() {
this._eventHandler.subscribe(this._dataView.onBeforePagingInfoChanged, () => {
this._wasRecheckedAfterPageChange = false; // reset the page check flag, to skip deletions on page change (used in code below)
});
this._eventHandler.subscribe(this._dataView.onPagingInfoChanged, () => {
// when user changes page, the selected row indexes might not show up
// we can check to make sure it is but it has to be in a delay so it happens after the first "onSelectedRowsChanged" is triggered
setTimeout(() => {
const shouldBeSelectedRowIndexes = this._dataView.mapIdsToRows(this._selectedRowDataContextIds);
const currentSelectedRowIndexes = this._grid.getSelectedRows();
if (!isequal(shouldBeSelectedRowIndexes, currentSelectedRowIndexes)) {
this._grid.setSelectedRows(shouldBeSelectedRowIndexes);
}
});
});
this._eventHandler.subscribe(this._grid.onSelectedRowsChanged, (e, args) => {
if (Array.isArray(args.rows) && Array.isArray(args.previousSelectedRows)) {
const newSelectedRows = args.rows;
const prevSelectedRows = args.previousSelectedRows;
const newSelectAdditions = newSelectedRows.filter((i) => prevSelectedRows.indexOf(i) < 0);
const newSelectDeletions = prevSelectedRows.filter((i) => newSelectedRows.indexOf(i) < 0);
// deletion might happen when user is changing page, if that is the case then skip the deletion since it's only a visual deletion
// if it's not a page change (when the flag is true), then proceed with the deletion in our global array of selected IDs
if (this._wasRecheckedAfterPageChange && newSelectDeletions.length > 0) {
const toDeleteDataIds = this._dataView.mapRowsToIds(newSelectDeletions);
toDeleteDataIds.forEach((removeId) => this._selectedRowDataContextIds.splice(this._selectedRowDataContextIds.indexOf(removeId), 1));
}
// if we have newly added selected row(s), let's update our global array of selected IDs
if (newSelectAdditions.length > 0) {
const toAddDataIds = this._dataView.mapRowsToIds(newSelectAdditions) || [];
toAddDataIds.forEach((dataId) => {
if (this._selectedRowDataContextIds.indexOf(dataId) === -1) {
this._selectedRowDataContextIds.push(dataId);
}
});
}
this._wasRecheckedAfterPageChange = true;
// form our full selected row IDs, let's make sure these indexes are selected in the grid, if not then let's call a reselect
// this could happen if the previous step was a page change
const shouldBeSelectedRowIndexes = this._dataView.mapIdsToRows(this._selectedRowDataContextIds);
const currentSelectedRowIndexes = this._grid.getSelectedRows();
if (!isequal(shouldBeSelectedRowIndexes, currentSelectedRowIndexes)) {
this._grid.setSelectedRows(shouldBeSelectedRowIndexes);
}
const newSelections = { gridRowIndexes: this._grid.getSelectedRows(), dataContextIds: this._selectedRowDataContextIds };
}
});
}
So in this code, the variable this._selectedRowDataContextIds
has the full set of selected data object IDs. It's a complex approach but that is what I found so far to be working with a grid Pagination.
Note that there's only 1 lodash
function isequal
that is used in the code to compare 2 arrays, everything else is written in ES6 and TypeScript (though I removed the types to be more readable)
Upvotes: 1
Reputation: 1
it works fine. Just modified the above one.
var selRows = grid.getSelectedRows();
var item = {};
for (var i = 0; i < selRows.length; i++) {
item = dataView.getItem(selRows[i]);
if (item != undefined && $.inArray(item.id, selectedRowIds) < 0 ) {
selectedRowIds.push(item.id);
}
}
var removeIds = [];
var row;
for (var i = 0; i < selectedRowIds.length; i++) {
for (var j = 0; j < selectedRowIds.length; j++) {
row = dataView.getRowById(selectedRowIds[j]);
if(row != undefined && $.inArray(row, removeIds) < 0){
removeIds.push(row);
break;
}
}
}
for (var i = 0; i < removeIds.length; i++) {
if (removeIds[i] != undefined && $.inArray(removeIds[i], selRows) < 0) {
item = dataView.getItem(removeIds[i]);
selectedRowIds = $.grep(selectedRowIds, function( a ) { return a !== item.id; });
}
}
Upvotes: 0
Reputation: 4514
Finally I had some success by adapting the grid.onSelectedRowsChanged callback in Example 4. The key difference is that I don't reset the global selectedRowIds
array each time the selection changes, since grid.getSelectedRows()
only gives me the selected row indices on the current page. Instead I first add all newly selected rows to the selectedRowIds
array and then iterate over it to check if one of its current-page-entries has been deselected.
grid.onSelectedRowsChanged.subscribe(function (e) {
var selRows = grid.getSelectedRows();
// add all newly selected rows to the selection
for (var i = 0, l = selRows.length; i < l; i++) {
var item = dataView.getItem(selRows[i]);
if (item) {
var rowId = item.id;
if (selectedRowIds.indexOf(rowId) < 0) {
selectedRowIds.push(rowId);
}
}
}
//check if a previously selected row has been deselected
for (var i = 0, l = selectedRowIds.length; i < l; i++) {
var row = dataView.getRowById(selectedRowIds[i]); // see if the selected row is available on the current page
if (row && selRows.indexOf(row) < 0) {
selectedRowIds.splice(i, 1); //remove row from array
}
}
});
Upvotes: 2
Reputation: 2921
Proposal:
Add an array of rows before grid definition.
var selectedRows = [];
In the pagging event, before all obtain and store selected rows if the row aren't in array yet:
function AddRows(grid.getSelectedRows()){//your comparision here};
In paggin event set selected rows:
grid.setSelectedRows(selectedRows );
Prevent deselect. If a row is deselect, get the row index in array, and remove the row from array:
function GetRowSelectedIndex(row);
var rowIndex = GetRowSelectedIndex(row);
selectedRows.slice(rowIndex,1);
Upvotes: 0