How to replace remapColums with remapColumnsByName in free jqgrid
Code from answer in how to persist current row in jqgrid
is used to save jqgrid state. It saves jqgrid column state using column numbers. If jqgrid colmodel is changed in server, this causes javascript error in browser.
Freeze rownum column in JQGrid comment and https://github.com/free-jqgrid/jqGrid/blob/master/README49.md describes method remapColumnsByName
. I hoped that using this fixes the issUe.
free jqgrid was downloaded from todays git master. In state save after columns was resized or moved line
saveColumnState.call($grid, $grid[0].p.remapColumns);
was changed to
saveColumnState.call($grid, $grid[0].p.remapColumnsByName);
and in state restore in loadComplete code
if (isColState && myColumnsState.permutation.length > 0 &&
myColumnsState.permutation.length === cm.length) {
$grid.jqGrid("remapColumns", myColumnsState.permutation, true);
}
with
if (isColState && myColumnsState.permutation.length > 0 &&
myColumnsState.permutation.length === cm.length) {
$grid.jqGrid("remapColumnsByName", myColumnsState.permutation, true);
}
Now line
if (isColState && myColumnsState.permutation.length > 0 &&
causes error
Uncaught TypeError: Cannot read property 'length' of undefined
How to fix this so that column state can can used if column definition is changed?
Methods are defined as
var saveColumnState = function (perm) {
var colModel = this.jqGrid('getGridParam', 'colModel'),
i, l = colModel.length, colItem, cmName,
postData = this.jqGrid('getGridParam', 'postData'),
columnsState = {
search: this.jqGrid('getGridParam', 'search'),
page: this.jqGrid('getGridParam', 'page'),
rowNum: this.jqGrid('getGridParam', 'rowNum'),
sortname: this.jqGrid('getGridParam', 'sortname'),
sortorder: this.jqGrid('getGridParam', 'sortorder'),
autoedit: autoedit,
rownumbers: $grid.jqGrid('getGridParam', 'rownumbers') && !$grid[0].p.colModel[0].hidden,
searchWindow: searchParams,
editWindow: editParams,
permutation: perm,
selectedRows: idsOfSelectedRows,
colStates: {}
},
colStates = columnsState.colStates;
if (typeof (postData.filters) !== 'undefined') {
columnsState.filters = postData.filters;
}
for (i = 0; i < l; i++) {
colItem = colModel[i];
cmName = colItem.name;
if (cmName !== 'rn' && cmName !== 'cb' && cmName !== 'subgrid') {
colStates[cmName] = {
width: colItem.width,
hidden: colItem.hidden
};
}
}
saveObjectInLocalStorage(myColumnStateName, columnsState);
};
var saveObjectInLocalStorage = function (storageItemName, object) {
if (typeof window.localStorage !== 'undefined') {
window.localStorage.setItem(storageItemName, JSON.stringify(object));
}
};
Solution 1:
First of all I want to mention that the code described in the old answer works not always correct. To explain the problem you can open the single row select demo for example and uses column chooser multiple times before reloading the grid. For example you can open column chooser first and change position of "Clients" column after "Tax" column. You will see correct results in the grid. Then you can open column chooser once more and move "Date" column after "Clients" column for example. You will see the columns in the order "Amount", "Tax", "Client", "Date", ... Now you can reload the page. You will see that the reloaded page have wrong order of columns: "Client", "Amount", "Tax", "Date", ... The reason on the problem: permutation
used by column chooser or by remapColumns
uses integer position of columns relatively to current order of columns. It makes saving of column order more complex. One have to hold original column order and recalculates always the values from permutation
array to reordering of original colModel
.
Alternatively one can saves column names instead of arrays with changed column position relatively to original column model. In other words one should replace permutation
property of columnsState
to something like cmOrder
with array of column names in the grid, which choosed the user last time.
The method remapColumnsByName
is very simple. It works like the method remapColumns
, but its first parameter is array of column names instead of array of integer indexes.
The demo is quick and dirty changing of the single row select demo to use cmOrder
property instead of permutation
property in columnsState
and to use the method remapColumnsByName
additionally. If you would repeat the same test like I described at the beginning of my answer you will see that new demo don't have the bug which I described before.
The most important parts of the demo which is different from original demo you will find below:
var getColumnNamesFromColModel = function () {
var colModel = this.jqGrid("getGridParam", "colModel");
return $.map(colModel, function (cm, iCol) {
// we remove "rn", "cb", "subgrid" columns to hold the column information
// independent from other jqGrid parameters
return $.inArray(cm.name, ["rn", "cb", "subgrid"]) >= 0 ? null : cm.name;
});
},
saveColumnState = function () {
var p = this.jqGrid("getGridParam"), colModel = p.colModel, i, l = colModel.length, colItem, cmName,
postData = p.postData,
columnsState = {
search: p.search,
page: p.page,
rowNum: p.rowNum,
sortname: p.sortname,
sortorder: p.sortorder,
cmOrder: getColumnNamesFromColModel.call(this),
selectedRows: idsOfSelectedRows,
colStates: {}
},
colStates = columnsState.colStates;
if (postData.filters !== undefined) {
columnsState.filters = postData.filters;
}
for (i = 0; i < l; i++) {
colItem = colModel[i];
cmName = colItem.name;
if (cmName !== "rn" && cmName !== "cb" && cmName !== "subgrid") {
colStates[cmName] = {
width: colItem.width,
hidden: colItem.hidden
};
}
}
saveObjectInLocalStorage(myColumnStateName(this), columnsState);
},
...
moreover the loadComplete
callback which restore the order of the columns is the following
loadComplete: function () {
var $this = $(this), p = $this.jqGrid("getGridParam"), i, count;
if (firstLoad) {
firstLoad = false;
if (isColState && myColumnsState.cmOrder != null && myColumnsState.cmOrder.length > 0) {
// We compares the values from myColumnsState.cmOrder array
// with the current names of colModel and remove wrong names. It could be
// required if the column model are changed and the values from the saved stated
// not corresponds to the
var fixedOrder = $.map(myColumnsState.cmOrder, function (name) {
return p.iColByName[name] === undefined ? null : name;
});
$this.jqGrid("remapColumnsByName", fixedOrder, true);
}
if (typeof (this.ftoolbar) !== "boolean" || !this.ftoolbar) {
// create toolbar if needed
$this.jqGrid("filterToolbar",
{stringResult: true, searchOnEnter: true, defaultSearch: myDefaultSearch});
}
}
refreshSerchingToolbar($this, myDefaultSearch);
for (i = 0, count = idsOfSelectedRows.length; i < count; i++) {
$this.jqGrid("setSelection", idsOfSelectedRows[i], false);
}
saveColumnState.call($this, this.p.remapColumns);
},
I want to repeat that the code from the new demo is far from be perfect. I just used the old code and fixed it to make it working in free jqGrid and by using new remapColumnsByName
method.