How to Replace Remapcolums with Remapcolumnsbyname in Free Jqgrid

How to replace remapColums with remapColumnsByName in free jqgrid

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.

JQGrid: Resetting the Columns to original Order and state

Restore hidden state of all columns is very simple and you found already the corresponding code examples. Thus I answer how to reset the column order.

The problem is that the method remapColumns which can be used to reorder the columns use column numbers instead of column names. The same problem exist in many other methods or internal parameters of jqGrid. To use the method remapColumns you need to use the index of columns based on the current index order. After the user changed the order of columns multiple times it would be difficult to provide the column numbers which you want to have relatively to the current column order. It would be much more easy to hold the names of columns instead of column indexes.

I develop free jqGrid as a fork of jqGrid after Tony had changed the licence agreement of jqGrid and renamed it to Guriddo jqGrid JS. I implemented many changes in jqGrid which disturbs my and have implemented many new features. One from the changes was moving all internal options to holding names instead of indexes as far it was possible without breaking the compatibility to the previous versions. I added the method remapColumnsByName, which simplifies the usage of column remapping (see the answer with the demo).

The implementation of your requirements in free jqGrid could be very simple. You need just save the original column order in an array and then use it as the parameter of remapColumnsByName to reset the columns

$("#grid1").jqGrid({
...
onInitGrid: function () {
var p = $(this).jqGrid("getGridParam");
// save names of columns in custom option of jqGrid
p.originalColumnOrder = $.map(p.colModel, function (cm) {
return cm.name;
});
//alert(JSON.stringify(p.originalColumnOrder));
}
}).jqGrid("navGrid", { add: false, edit: false, del: false })
.jqGrid("navButtonAdd", {
buttonicon: "fa-repeat",
caption: "",
title: "Reset original column order",
onClickButton: function () {
var $self = $(this), p = $self.jqGrid("getGridParam");
$self.jqGrid("remapColumnsByName", p.originalColumnOrder, true);
}});

See the demo https://jsfiddle.net/OlegKi/r6b2os5b/2/

If you can't migrate to free jqGrid because of some reasons then you have to simulate the same behavior. You should save original column order as array of column names and then to convert the names to indexes directly before calling of remapColumns. It should work too.

jqGrid to reorder column by index name dynamically

Please write allays which version of jqGrid you use (or can use) and from, which fork of jqGrid. Moreover it's important to write which fork of jqGrid you use (free jqGrid, commercial Gurrido jqGrid JS or an old jqGrid in version <=4.7).

I agree with the problem of the usage column indexes instead of column names. It's the reason why I made many changes in the free jqGrid fork, which I develop starting with making the main old fork commercial (see the post) in the version 4.7.1. I introduced the method remapColumnsByName together with the old method remapColumns. The answer provides the demo, which demonstrates the usage of remapColumnsByName. Moreover free jqGrid holds mostly column names instead of column indexes internally and it has the helper option iColByName, which helps to get the current index of the column by the column name.

I recommend you to upgrade jqGrid, which you use to the current (4.13.4) version of free jqGrid and to use remapColumnsByName.

Where can I use jqgrid remapColumns function?

In the answer you can find an example how to use remapColumns and I hope another helpful information for you. In the demo I use remapColumns once inside of loadComplete. I don't understand why you want to change the column order on every request to the server.

UPDATED: The modified demo don't have the effect which you described. The reason was that one called in the old demo the method remapColumns with empty ([]) permutation parameter and remapColumns works incorrect in the case. So I included additional test for myColumnsState.permutation.length > 0.

UPDATED 2: Free jqGrid provides the method remapColumnsByName additionally to remapColumns. The first parameter of remapColumnsByName is array of column names, which should be on the grid. Columns "subgrid", "cb" and "rn" could be included or not. The understanding of the usage of remapColumnsByName is much more easy as the usage of remapColumns especially in situations where the order of columns could be changes by the user (by columnChooser, wor example). It's strictly recommended to use remapColumnsByName instead of remapColumns.

how to persist current row in jqgrid

I combined the code from the previous answer about persisting jqGrid column preferences with the code of from another answer where I suggested the code which implemented persistent selection of rows. It's important to mention, that in case of multiselect:true it will be used the array of ids of selected rows which contains all selected even if the rows are on another page. It's very practical and the implementation very simple. So I posted the corresponding feature request, but it's stay till now unanswered.

Now I can present two demos: the first demo which use multiselect: true and the second demo which uses the same code, but with the single selection.

The most important parts of the code which I used you will find below.

One thing is very important to mention: you should modify the value of myColumnStateName in every page which you use. The value of the variable contain the name of the column state in the localStorage. So it you would not change the name you will share state of different tables which can follows to very strange effects. You can consider to use names constructed from the name of the current page or it's URL as the value of myColumnStateName.

var $grid = $("#list"),
getColumnIndex = function (grid, columnIndex) {
var cm = grid.jqGrid('getGridParam', 'colModel'), i, l = cm.length;
for (i = 0; i < l; i++) {
if ((cm[i].index || cm[i].name) === columnIndex) {
return i; // return the colModel index
}
}
return -1;
},
refreshSerchingToolbar = function ($grid, myDefaultSearch) {
var postData = $grid.jqGrid('getGridParam', 'postData'), filters, i, l,
rules, rule, iCol, cm = $grid.jqGrid('getGridParam', 'colModel'),
cmi, control, tagName;

for (i = 0, l = cm.length; i < l; i++) {
control = $("#gs_" + $.jgrid.jqID(cm[i].name));
if (control.length > 0) {
tagName = control[0].tagName.toUpperCase();
if (tagName === "SELECT") { // && cmi.stype === "select"
control.find("option[value='']")
.attr('selected', 'selected');
} else if (tagName === "INPUT") {
control.val('');
}
}
}

if (typeof (postData.filters) === "string" &&
typeof ($grid[0].ftoolbar) === "boolean" && $grid[0].ftoolbar) {

filters = $.parseJSON(postData.filters);
if (filters && filters.groupOp === "AND" && typeof (filters.groups) === "undefined") {
// only in case of advance searching without grouping we import filters in the
// searching toolbar
rules = filters.rules;
for (i = 0, l = rules.length; i < l; i++) {
rule = rules[i];
iCol = getColumnIndex($grid, rule.field);
if (iCol >= 0) {
cmi = cm[iCol];
control = $("#gs_" + $.jgrid.jqID(cmi.name));
if (control.length > 0 &&
(((typeof (cmi.searchoptions) === "undefined" ||
typeof (cmi.searchoptions.sopt) === "undefined")
&& rule.op === myDefaultSearch) ||
(typeof (cmi.searchoptions) === "object" &&
$.isArray(cmi.searchoptions.sopt) &&
cmi.searchoptions.sopt.length > 0 &&
cmi.searchoptions.sopt[0] === rule.op))) {
tagName = control[0].tagName.toUpperCase();
if (tagName === "SELECT") { // && cmi.stype === "select"
control.find("option[value='" + $.jgrid.jqID(rule.data) + "']")
.attr('selected', 'selected');
} else if (tagName === "INPUT") {
control.val(rule.data);
}
}
}
}
}
}
},
saveObjectInLocalStorage = function (storageItemName, object) {
if (typeof window.localStorage !== 'undefined') {
window.localStorage.setItem(storageItemName, JSON.stringify(object));
}
},
removeObjectFromLocalStorage = function (storageItemName) {
if (typeof window.localStorage !== 'undefined') {
window.localStorage.removeItem(storageItemName);
}
},
getObjectFromLocalStorage = function (storageItemName) {
if (typeof window.localStorage !== 'undefined') {
return JSON.parse(window.localStorage.getItem(storageItemName));
}
},
myColumnStateName = 'ColumnChooserAndLocalStorage2.colState',
idsOfSelectedRows = [],
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'),
sortname: this.jqGrid('getGridParam', 'sortname'),
sortorder: this.jqGrid('getGridParam', 'sortorder'),
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);
},
myColumnsState,
isColState,
restoreColumnState = function (colModel) {
var colItem, i, l = colModel.length, colStates, cmName,
columnsState = getObjectFromLocalStorage(myColumnStateName);

if (columnsState) {
colStates = columnsState.colStates;
for (i = 0; i < l; i++) {
colItem = colModel[i];
cmName = colItem.name;
if (cmName !== 'rn' && cmName !== 'cb' && cmName !== 'subgrid') {
colModel[i] = $.extend(true, {}, colModel[i], colStates[cmName]);
}
}
}
return columnsState;
},
updateIdsOfSelectedRows = function (id, isSelected) {
var index = idsOfSelectedRows.indexOf(id);
if (!isSelected && index >= 0) {
idsOfSelectedRows.splice(index, 1); // remove id from the list
} else if (index < 0) {
idsOfSelectedRows.push(id);
}
},
firstLoad = true;

myColumnsState = restoreColumnState(cm);
isColState = typeof (myColumnsState) !== 'undefined' && myColumnsState !== null;
idsOfSelectedRows = isColState && typeof (myColumnsState.selectedRows) !== "undefined" ? myColumnsState.selectedRows : [];

$grid.jqGrid({
// ... some options
page: isColState ? myColumnsState.page : 1,
search: isColState ? myColumnsState.search : false,
postData: isColState ? { filters: myColumnsState.filters } : {},
sortname: isColState ? myColumnsState.sortname : 'invdate',
sortorder: isColState ? myColumnsState.sortorder : 'desc',
onSelectRow: function (id, isSelected) {
updateIdsOfSelectedRows(id, isSelected);
saveColumnState.call($grid, $grid[0].p.remapColumns);
},
onSelectAll: function (aRowids, isSelected) {
var i, count, id;
for (i = 0, count = aRowids.length; i < count; i++) {
id = aRowids[i];
updateIdsOfSelectedRows(id, isSelected);
}
saveColumnState.call($grid, $grid[0].p.remapColumns);
},
loadComplete: function () {
var $this = $(this), i, count;

if (firstLoad) {
firstLoad = false;
if (isColState) {
$this.jqGrid("remapColumns", myColumnsState.permutation, 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);
},
resizeStop: function () {
saveColumnState.call($grid, $grid[0].p.remapColumns);
}
});

$grid.jqGrid('navGrid', '#pager', {edit: false, add: false, del: false});
$grid.jqGrid('navButtonAdd', '#pager', {
caption: "",
buttonicon: "ui-icon-closethick",
title: "clear saved grid's settings",
onClickButton: function () {
removeObjectFromLocalStorage(myColumnStateName);
window.location.reload();
}
});

UPDATED: I forgot to mention that in case of usage multiselect: true option with jqGrid 4.3 it is very important to use the fix which described here. In the first demo I used the modified version of the jquery.jqGrid.src.js which include the bug fix.

UPDATED 2: To make easy to generate unique name of the local storage item used to save the grid state I modified the demos a little. The next version of the multiselect demo and the single select demo use myColumnStateName as the function defined as the following

var myColumnStateName = function (grid) {
return window.location.pathname + '#' + grid[0].id;
}

The usage of myColumnStateName are changed correspondingly. Additionally I extended the column state to save the rowNum value.

UPDATED 3: The answer describe how one can use new possibility of free jqGrid to save the grid state.

Avoiding data corruption if column state is saved, new column is defined in server and data is saved in edit

I know the problem very good! One need to implement some kind of validating checks of the previously saved state of the grid before the usage. The deepness of checks could depend on the exact requirements of your application and from the information which one knows exactly. The most opened and unclear thing: should one make some correction/fixing of the previously saved state or should one discard the state on the first small error? The answer on the question depends on the project where jqGrid are used. Deep fixing could include fixing of sorting parameter and modifying previously saved filter. Another example: the state could include ids of selected rows, but the fixing of the part of the state could be bad idea in the common case. One loading of the data could imply one setting of selected rows, but loading of another data (unfiltered for example) could do have the rows and the rows should be do selected. There are no best choice in the case, all depends on the exact project requirements. In any way the implementation of the state validation/fixing isn't a simple code.

Only because of the complexity of the problems of validation of previously saved state and the existence of different scenarios of validation I didn't implemented such feature in free jqGrid. Any good implementation needs time and the resulting code will be not simple. It will have some options for some typical scenarios. I would like to implement the feature in the future, but I just didn't found the time for the implementation, because I have to do my main job to earn money for my family and I still try to help other people in the community who have small, but important, for the person, problems with jqGrid of free jqGrid.

free-jqgrid: easier way to save, load and apply filter data including filter-toolbar text and page-settings?

The answer with the demo provides an example of the implementation. The method refreshSerchingToolbar is relatively long. On the other side the code is still not full. It's not restore the state of operands, if the option searchOperators: true is used. I wanted to rewrite many parts of the filterToolbar method to make easier the saving/restoring of the the state of the filter toolbar or refreshing based on the changed postData. It's just the problem of the time and nothing more. What you describe is close to the feature forceClientSorting: true, which was difficult to implement in original code of jqGrid 4.7, but it was easy to implement after I rewrote large part of processing of the server response. The same problem is with the code of filterToolbar. The changes of filterToolbar is still in my TODO list and I'll made the corresponding changes soon.

Persisting Row order and row width, free-jqGrid

it' difficult to answer on questions with long code without having the possibility to debug it. Nevertheless I suppose that your problem is in the usage of restoreColumnState which you call in synchronous way: myColumnsState = restoreColumnState.call($grid, cm); in the code, but it calls getObjectFromLocalStorage which works asynchronously. The data return $.parseJSON(data.colState); returned from success callback of jquery.ajax will be just ignored. To fit the code you can rewrite the code to create the whole grid with the used state inside of success callback.

free-jqGrid External Filtering Used With Grid's beforeRequest() or onPaging() Event

It's difficult to answer on your question because you didn't posted code fragments, which shows how you use jqGrid and because the total number of data, which could be needed to display in all pages isn't known.

In general there are two main alternatives implementing of custom filtering:

  • server side filtering
  • client side filtering

One can additionally use a mix from both filtering. For example, one can load from the server all invoices based on some fixed filters (all invoices of specific user or all invoices of one organization, all invoices of the last month) and then use loadonce: true, forceClientSorting: true options to sort and to filter the returned data on the client side. The user could additionally to filter the subset of data locally using filter toolbar of searching dialog.

The performance of client side is essentially improved last years and loading relatively large JSON data from the server could be done very quickly. Because of that Client-Side-Filtering is strictly recommended. For better understanding the performance of local sorting, filtering and paging I'd recommend you to try the functionality on the demo. You will see that the timing of local filtering of the grid with 5000 rows and 13 columns is better as you can expect mostly from the round trip to the server and processing of server side filtering on some very good organized database. It's the reason why I recommend to consider to use client side sorting (or loadonce: true, forceClientSorting: true options) as far it's possible.

If you need to filter data on the server then you need just send additional parameters to the server on every request. One can do that by including additional parameters in postData. See the old answer for additional details. Alternatively one can use serializeGridData to extend/modify the data, which will be set to the server.

After the data are loaded from the server, it could be sorted and filtered locally before the first page of data will be displayed in the grid. To force local filtering one need just add forceClientSorting: true additionally to well known loadonce: true parameter. It force applying local logic on the data returned from the server. Thus one can use postData.filters, search: true to force additional local filtering and sortname and sortorder parameter to force local sorting.

One more important remark about using hidden columns. Every hidden column will force creating DOM elements, which represent unneeded <td> elements. The more DOM elements you place on the page the more slow will be the page. If local data will be used (or if loadonce: true be used) then jqGrid hold data associated with every row twice: once as JavaScript object and once as cells in the grid (<td> elements). Free jqGrid allows to use "additional properties" instead of hidden columns. In the case no data will be placed in DOM of the grid, but the data will be hold in JavaScript objects and one able to sort or filter by additional properties in the same way like with other columns. In the simplest way one can remove all hidden columns and to add additionalProperties parameter, which should be array of strings with the name of additional properties. Instead of strings elements of additionalProperties could be objects of the same structures like colModel. For example, additionalProperties: [{ name: "taskId", sorttype: "integer"}, "isFinal"]. See the demo as an example. The input data of the grid can be seen here. Another demo shows that searching dialog contains additional properties additionally to jqGrid column. The commented part columns of searching shows more advanced way to specify the list and the order of columns and additional properties displayed in searching dialog.



Related Topics



Leave a reply



Submit