Column filtering using select menus(DataTables) - asp.net-mvc-3

Good day!
I`m using MVC3 and jquery DataTables plugin. The point is to make column filtering with select menus(further multi-select).
Here is my JS, which is pretty similar to this DataTables example:
(function ($) {
$.fn.dataTableExt.oApi.fnGetColumnData = function (oSettings, iColumn, bUnique, bFiltered, bIgnoreEmpty) {
// check that we have a column id
if (typeof iColumn == "undefined") return new Array();
// by default we only want unique data
if (typeof bUnique == "undefined") bUnique = true;
// by default we do want to only look at filtered data
if (typeof bFiltered == "undefined") bFiltered = true;
// by default we do not want to include empty values
if (typeof bIgnoreEmpty == "undefined") bIgnoreEmpty = true;
// list of rows which we're going to loop through
var aiRows;
// use only filtered rows
if (bFiltered == true) aiRows = oSettings.aiDisplay;
// use all rows
else aiRows = oSettings.aiDisplayMaster; // all row numbers
// set up data array
var asResultData = new Array();
for (var i = 0, c = aiRows.length; i < c; i++) {
iRow = aiRows[i];
var aData = this.fnGetData(iRow);
var sValue = aData[iColumn];
// ignore empty values?
if (bIgnoreEmpty == true && sValue.length == 0) continue;
// ignore unique values?
else if (bUnique == true && jQuery.inArray(sValue, asResultData) > -1) continue;
// else push the value onto the result data array
else asResultData.push(sValue);
}
return asResultData;
}
} (jQuery));
function fnCreateSelect(aData) {
var r = '<select><option value=""></option>', i, iLen = aData.length;
for (i = 0; i < iLen; i++) {
r += '<option value="' + aData[i] + '">' + aData[i] + '</option>';
}
return r + '</select>';
}
$(document).ready(function () {
var oTable = $('#dataTable').dataTable({
"sDom": 'W<"clear">lfrtip',
"sAjaxSource": '#Url.Action("ResourcesWorkflowData", "LineManager")',
"aoColumns": [
{ "sTitle": "User", "mData": "User" },
{ "sTitle": "Region", "mData": "Region" },
]
});
/* Add a select menu for each TH element in the table footer */
$("tfoot th").each(function (i) {
this.innerHTML = fnCreateSelect(oTable.fnGetColumnData(i));
$('select', this).change(function () {
oTable.fnFilter($(this).val(), i);
});
});
});
The problem is in my aaData. If i pass an array of arrays like this:
"aaData": [
['User1', 'Central'],
['User2', 'Central'],
]
Everything goes fine, but if i make array of objects like this:
"aaData": [
{
"User": "User1",
"Region": "Central",
},
{
"User": "User2",
"Region": "Central",
}
]
I get "Cannot read property 'length' of undefined " error in this line of js:
// ignore empty values?
if (bIgnoreEmpty == true && sValue.length == 0) continue;
Why is this happening? I was trying to use some Add Ons like ColumnFilter or ColumnFilterWidgets but in both cases i got the some problem. Any suggestions please?

It looks like sValue is null, which means that one of your field in Json is null. Can you paste your full Json?
There is small hack for this kind of problem.
Just to make sure non fields are null you can write something simple like this:
User = SomeObject.User != null ? SomeObject.User : " ";
This should fix your issue as value won't be null anymore.

Related

How do I add html tags in jquery plugins?

I am doing the live search using the jquery plugins. When I tried to search that doesn't exist, it only shows the table. I would like to put some message "No result found" if it doesnt exist. The question is how can I add message "No result found"
Note: In my codes I add some validation, the user need input minimum of 3 characters
/**
**options to have following keys:
**searchText: this should hold the value of search text
**searchPlaceHolder: this should hold the value of search input box placeholder
**/
(function($)
{
$.fn.tableSearch = function(options)
{
if(!$(this).is('table'))
{
return;
}
var tableObj = $(this),
searchText = (options.searchText)?options.searchText:'Search: ',
searchPlaceHolder = (options.searchPlaceHolder)?options.searchPlaceHolder:'',
divObj = $('<div style="font-size:20px;">'+searchText+'</div><br /><br />'),
inputObj = $('<input style="min-width:25%;max-width:50%;margin-left:1%" type="text" placeholder="'+searchPlaceHolder+'" />'),
caseSensitive = (options.caseSensitive===true)?true:false,
searchFieldVal = '',
pattern = '';
inputObj.off('keyup').on('keyup', function(){
searchFieldVal = $(this).val();
if(searchFieldVal.length == 0)
{
tableObj.find('tbody tr').show();
}
else if(searchFieldVal.length >= 3)
{
pattern = (caseSensitive)?RegExp(searchFieldVal):RegExp(searchFieldVal, 'i');
tableObj.find('tbody tr').hide().each(function()
{
var currentRow = $(this);
currentRow.find('td').each(function()
{
var result = "No result";
$("tbody tr").append(result);
if(pattern.test($(this).html()))
{
currentRow.show();
return false;
}
});
});
}
});
tableObj.before(divObj.append(inputObj));
return tableObj;
}
}(jQuery));
Here into JQ plugin(Posted at your question), the handler for empty result is exist. See piece of code from it.
else if(searchFieldVal.length >= 3)
{
pattern = (caseSensitive)?RegExp(searchFieldVal):RegExp(searchFieldVal, 'i');
tableObj.find('tbody tr').hide().each(function()
{
var currentRow = $(this);
currentRow.find('td').each(function()
{
var result = "No result";
$("tbody tr").append(result);
if(pattern.test($(this).html()))
{
currentRow.show();
return false;
}
});
});
}
Paraphrase you mistaken at your end. Re check it.

context: contextMenu.SelectionContext() not working for selected text in input fields

My little project - extension for Firefox that translate text. User select text on page, make right click and see translation right in the context-menu or in popup. In my contextMenu.Item I am using context: contextMenu.SelectionContext() for determin context (for example, user click on image or on selected text).
But this not working if text selected in input field, documentation page not mentioned it. What I need to do for handling selection context for input fields, not only for regular text on page? In this situation I see my context-menu, but in debug I see that selected text was not send to the code of my extension.
I tried this code - nothing.
My current code is:
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
var uuid = require('sdk/util/uuid').uuid();
var uuidstr = uuid.number.substring(1, 37);
var notifications = require("sdk/notifications");
var contextMenu = require("sdk/context-menu");
var Request = require("sdk/request").Request;
var self = require('sdk/self');
var tabs = require('sdk/tabs');
var prefs = require('sdk/simple-prefs').prefs;
var cmitems = null;
var wasTranslatedSecondTime = false;
var inProgress = '...';
var translated = '';
var menuItem = contextMenu.Item({
data: uuidstr, // for 'binding' tooltop's 'id' + text
label: inProgress, // ...
image: self.data.url('ico.png'),
context: contextMenu.SelectionContext(),
contentScript: 'self.on("context", function() {' +
'var selectionText = window.getSelection().toString();' +
'self.postMessage({name:"context", data:selectionText});' +
'return true;' +
'});' +
'self.on("click", function() {' +
'var selectionText = window.getSelection().toString();' +
'self.postMessage({name:"click", data:"https://translate.yandex.ru?text=" + selectionText.replace("&", "%26")});' +
'})',
onMessage: function(message) {
if (message.name == 'context') {
menuItem.label = inProgress; // ...
if (cmitems != undefined) cmitems[0].tooltipText = '';
var input = message.data.replace('&', '%26');
translate('ru', input); // default direction - from EN to RU
} else { // if (message.name == 'click')
tabs.open(message.data);
}
}
});
function translate(lang, input) {
Request({ // key is not referral but API-key: https://api.yandex.com/translate/doc/dg/concepts/api-overview.xml
url: 'https://translate.yandex.net/api/v1.5/tr.json/translate?key=trnsl.1.1.20150627T071448Z.117dacaac1e63b79.6b1b4bb84635161fcd400dace9fb2220d6f344ef&lang=' +
lang + '&text=' + input,
onComplete: function (response) {
translated = response.json.text[0];
if (input == translated && wasTranslatedSecondTime == false) { // if input on Russian and we receive the same text -
translate('en', input); // translate again selected text into English
wasTranslatedSecondTime = true;
} else { // show results
if (prefs.popup) popup(translated);
menuItem.label = translated;
wasTranslatedSecondTime = false;
if (prefs.tooltip) tooltip(translated);
}
}
}).get();
}
function popup(text) {
if (text.length > 0)
notifications.notify({
title: 'translate.yandex.ru',
text: text,
time: 5000
})
}
function tooltip(translated) {
menuItem.data = uuidstr + translated;
cmitems = getMostRecentBrowserWindow().document.querySelectorAll(".addon-context-menu-item[value^='"+uuidstr+"']");
cmitems[0].tooltipText = cmitems[0].value.substring(36);
}
It appears that your question boils down to: How do I get the selected text, even when it is in an input field?
You are currently using var selectionText = window.getSelection().toString(); which is failing when the selected text is in an input field.
In one of my extensions, I use the following to obtain the selected text. It works even when the selected text is in an input field:
/**
* Fix an issue with Firefox that it does not return the text from a selection if
* the selected text is in an INPUT/textbox.
*/
function getSelectedText(win,doc) {
//Adapted from a post by jscher2000 at:
// http://forums.mozillazine.org/viewtopic.php?f=25&t=2268557
//Is supposed to solve the issue of Firefox not getting the text of a selection when
// it is in a textarea/input/textbox.
var ta;
if (win.getSelection && doc.activeElement){
if (doc.activeElement.nodeName == "TEXTAREA" ||
(doc.activeElement.nodeName == "INPUT" &&
doc.activeElement.getAttribute("type").toLowerCase() == "text")
){
ta = doc.activeElement;
return ta.value.substring(ta.selectionStart, ta.selectionEnd);
} else {
//As of Firefox 31.0 this appears to have changed, again.
//Try multiple methods to cover bases with different versions of Firefox.
let returnValue = "";
if (typeof win.getSelection === "function"){
returnValue = win.getSelection().toString();
if(typeof returnValue === "string" && returnValue.length >0) {
return returnValue
}
} //else
if (typeof doc.getSelection === "function"){
returnValue = doc.getSelection().toString();
if(typeof returnValue === "string" && returnValue.length >0) {
return returnValue
}
} //else
if (typeof win.content.getSelection === "function"){
returnValue = win.content.getSelection().toString();
if(typeof returnValue === "string" && returnValue.length >0) {
return returnValue
}
} //else
//It appears we did not find any selected text.
return "";
}
} else {
return doc.getSelection().toString();
}
}

Filter plugin without data-attribute

How to filter or sort info without data-attribute by plugin of MixItUp/Isototpe?
Maybe filter will be an #url and sort will be class.
Assuming you have a JSON set, you can filter against the values like so (I'm also assuming you have taken the filter/sort value from the DOM already):
Haven't run this so apologies if not exact - and I'm not used to stack overflow formatting yet but any issues and I'll edit. I think the logic should be OK
var dataset = [
{url: '/test'},
{url: '/test2'}
];
var filterValue = '/test2'; // pass this to function or get from DOM
var sortBy = 'url'; // again, pass in somewhere or get from DOM
var sortType = 'asc';
function filter(data, filter) {
var filteredResults = data.filter(function (el) {
return (filter !== '' && el.url.toLowerCase().indexOf(filter.toLowerCase())) !== -1;
})
return filteredResults;
}
function sort(data, sortBy, sortType) {
var sortIndex = sortType === "asc" ? 1 : -1;
data.sort(function (a, b) {
if (a[sortBy] < b[sortBy]) {
return -1 * sortIndex;
} else if (a[sortBy] > b[sortBy]) {
return 1 * sortIndex;
}
return 0;
})
}
var filteredResults = filter(dataset, filterValue);
sort(dataset, sortBy, sortType); //dataset will be sorted

SlickGrid filter not working

I am fairly new to SlickGrid. I am trying to make SlickGrid filter work but no luck. I am following the example (http://mleibman.github.io/SlickGrid/examples/example4-model.html).
Below is my source code.
$(document).ready(function() {
var tName;
$('#submit').click(function(e) {
tName = $('#source option:selected').text();// name1
tName = tName.trim();
$.ajax({
url : 'someUrl',
type : 'GET',
cache : false,
success : function(d) {
var grid;
var searchString = "";
var data = [];
var columns = new Array();
var cols = d[0].columns;
var pkColNames = d[0].pkColNames;
for (var j=0; j< cols.length; j++) {
var key = {id: cols[j].colName, name: cols[j].colName, field: cols[j].colName, width: 200, sortable:true, editor: Slick.Editors.LongText};
columns[j] = key;
}
var options = {
editable: true,
enableAddRow: false,
enableCellNavigation: true,
asyncEditorLoading: false,
enableColumnReorder:true,
multiColumnSort: false,
autoEdit: false,
autoHeight: false
};
function myFilter(item, args) {
return true;// Let us return true all time ?
}
for (var i = 0; i < d.length; i++) {
var tempData = (data[i] = {});
var title = null;
var val = null;
for (var q = 0; q < d[i].columns.length; q++) {
title = d[i].columns[q].colName;
val = d[i].columns[q].colValue;
tempData[title] = val;
}
}
var dataView = new Slick.Data.DataView({ inlineFilters: true });
grid = new Slick.Grid("#myGrid", dataView, columns, options);
dataView.setPagingOptions({
pageSize: 25
});
var pager = new Slick.Controls.Pager(dataView, grid, $("#myPager"));
var columnpicker = new Slick.Controls.ColumnPicker(columns, grid, options);
grid.setSelectionModel(new Slick.CellSelectionModel());
grid.onAddNewRow.subscribe(function(e, args) {
// Adding a new record is not yet decided.
});
grid.onCellChange.subscribe(function (e) {
var editedCellNo = arguments[1].cell;
var editedColName = grid.getColumns()[editedCellNo].field;
var newUpdatedValue= arguments[1].item[grid.getColumns()[editedCellNo].field];
var editedColType = "";
for (var cnt = 0; cnt < cols.length; cnt++) {
if (editedColName == cols[cnt].colName) {
editedColType = cols[cnt].colType;
break;
}
}
var pkKeyValue="";
for (var v=0; v <pkColNames.length;v++) {
for (var p=0; p<grid.getColumns().length; p++) {
if (pkColNames[v] == grid.getColumns()[p].field) {
var value = arguments[1].item[grid.getColumns()[p].field];
pkKeyValue += "{"+pkColNames[v] + '~' +getColDbType(grid.getColumns()[p].field) + ":"+value+"},";
break;
}
}
}
function getColDbType(colName) {
for (var c = 0; c < cols.length; c++) {
if (colName == cols[c].colName) {
return cols[c].colType;
}
}
}
pkKeyValue = pkKeyValue.substring(0, pkKeyValue.length-1);
$.ajax({
url: 'anotherUrl',
type:'GET',
dataType:'text',
success: function(f) {
bootbox.alert("Data updated successfully");
},
error: function() {
bootbox.alert("Error - updating data. Please ensure you are adding the data in right format.");
grid.invalidateAllRows();
grid.render();
}
});
});
grid.onClick.subscribe(function (e) {
//do-nothing
});
dataView.onRowsChanged.subscribe(function(e, args) {
grid.updateRowCount();
grid.invalidateRows(args.rows);
grid.render();
});
grid.onSort.subscribe(function(e, args) {
// args.multiColumnSort indicates whether or not this is a multi-column sort.
// If it is, args.sortCols will have an array of {sortCol:..., sortAsc:...} objects.
// If not, the sort column and direction will be in args.sortCol & args.sortAsc.
// We'll use a simple comparer function here.
var comparer = function(a, b) {
return a[args.sortCol.field] > b[args.sortCol.field];
}
// Delegate the sorting to DataView.
// This will fire the change events and update the grid.
dataView.sort(comparer, args.sortAsc);
});
// wire up the search textbox to apply the filter to the model
$("#txtSearch").keyup(function (e) {
console.log('Called...txtSearch');
Slick.GlobalEditorLock.cancelCurrentEdit();
// clear on Esc
if (e.which == 27) {
this.value = "";
}
searchString = this.value;
updateFilter();
});
function updateFilter() {
console.log("updateFilter");
dataView.setFilterArgs({
searchString: searchString
});
dataView.refresh();
}
dataView.beginUpdate();
dataView.setItems(data, pkColNames);
dataView.setFilterArgs({
searchString: searchString
});
dataView.setFilter(myFilter);
dataView.endUpdate();
},
error : function() {
bootbox.alert("Invalid user");
}
});
});
});
Your function myFilter() is always returning true so of course it will never work. The example that you looked at, was to filter something specific. I would recommend that you look at the following example to have a generic filter. From the example, simply type the text you are looking on a chosen column and look at the result... see example below (from SlickGrid examples).Using fixed header row for quick filters
In case you want a more in depth conditional filters ( > 10, <> 10, etc...), please take a look at my previous answer on how to make this kind of filtering possible, see my previous answer below:SlickGrid column type
Hope that helps you out

how to persist current row in jqgrid

How to presist current row if grid is opened again or page is refreshed ?
Answer in Persisting jqGrid column preferences describes how to persist column width and some other parameters.
In this answer demo I clicked in some row and pressed F5 . Previous clicked row was not highlighted.
How to save / restore current row in local storage ?
Update
If jqGrid column structure is modified in application and user opens application from browser again,
restorecolumnstate creates invalid colmodel where some elements are missing. This causes exception in refreshSearchingToolbar which assumes that all colmodel elements are present.
How to fix this ? How to dedect modified colmodol and not to restore colmodel in this case ? Or should restoreColumnState update colModel so that proper array is created ?
**Update 2 **
If myColumnsState.permutation contains nulls $grid.jqGrid("remapColumns", myColumnsState.permutation, true) created invalid colmodel. Here are screenshots from VS debugger immediately before and after remapColumns call
after:
I fixed this by chaning code to
if (isColState && myColumnsState.permutation.length > 0) {
var i, isnull = false;
for (i = 0; i < myColumnsState.permutation.length; i = i + 1) {
if (myColumnsState.permutation[i] == null) {
isnull = true;
break;
}
}
if (!isnull) {
$grid.jqGrid("remapColumns", myColumnsState.permutation, true);
}
Is this best solution ?
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.
Oleg's solution generates an error when you refresh the page like below.
Error: Uncaught TypeError: Cannot read property 'el' of undefined
Line: 1936 in jquery.jqGrid.src.js
var previousSelectedTh = ts.grid.headers[ts.p.lastsort].el, newSelectedTh = ts.grid.headers[idxcol].el;
Solution to this is to save the lastsort grid parameter and reset it when load complete like below.
saveColumnState = function(perm) {
...
columnsState = {
search: this.jqGrid('getGridParam', 'search'),
page: this.jqGrid('getGridParam', 'page'),
sortname: this.jqGrid('getGridParam', 'sortname'),
sortorder: this.jqGrid('getGridParam', 'sortorder'),
lastsort: this.jqGrid('getGridParam', 'lastsort'),
permutation: perm,
colStates: { }
},
...
},
loadComplete: function(data) {
...
if (isColState) {
$this.jqGrid("remapColumns", myColumnsState.permutation, true);
if(myColumnsState.lastsort > -1)
$this.jqGrid("setGridParam", { lastsort: myColumnsState.lastsort });
}
...
},

Resources