how to persist current row in jqgrid
Asked Answered
R

2

7

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

enter image description here

after:

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 ?

Registered answered 17/12, 2011 at 16:1 Comment(0)
M
8

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.

Meridional answered 17/12, 2011 at 20:54 Comment(24)
Critical patch from trirand.com/blog/?page_id=393/bugs/… is not applied. Everysuccessful remote delete causes exception.Registered
@Andrus: In the demos which I posted there are no Delete, Add or Edit operation. How you can try no exception will be thrown.Meridional
tis was general comment, sorry. How to re-use this code for multiple grids in same and different pages ? Can jqgrid extended or subclassed with this code or other idea?Registered
if only pager navigation buttons pressed, current page number is not saved. Page size is not saved.Registered
@Andrus: To have less misunderstanding how to use myColumnStateName in case of many grids on the page I modified the demos a little. Additionally I extended the column state to save the rowNum value.Meridional
Thank you very much. In my case grid is constructed from one querystring parameter so I cannot use this column state name creation. I added var myColumnStateName to site master and assign it in index.aspx file. Works if page contains single grid only.Registered
@Andrus: You should just modify the algorithm of assignment of myColumnStateName so that the most important part of querystring will be included in the myColumnStateName. Probably you should set the myColumnStateName in the ASPX file instead of the site master page.Meridional
thank you. Page contains two grid whose settigns needs persisted. Should I duplicate all this code for both grids or is it possible to re-use code without duplication?Registered
@Andrus: Sorry, but I don't understand your current problem. The last version of the code of saveColumnState and restoreColumnState can be used with different grids. Instead of saveColumnState.call($grid, $grid[0].p.remapColumns); you can also use saveColumnState.call($(this), this.p.remapColumns); in the most cases which allow to share the code more easy between the grids. So you should not duplicate the code, just use it for two grids.Meridional
If multiboxonly: true is used and selection is cleared by clickin in some other column, pressing F5 shows previously selected rows also. How to persist selected columns if multiboxonly: true setting is used ?Registered
If sort column name is not specified or wrong, jGrid throws error on such sort colun name. I created pull request to fix this github.com/kobruleht/jqGrid/commit/… For unknow reason it looks like this was merged with previous requestf, actually those are two different onesRegistered
if grid columns removed in application code, restorecolumnsState creates invalid colModel where come array elements are missing. How to fix this ? I updated question.Registered
@Andrus: Sorry, but I don't understand what you mean. How you remove the column? jqGrid supports only hiding and no removing of the columns. Which restorecolumnsState function you mean? I don't find it in my code.Meridional
It looks like is was related to permutation contianing null values. I updated question, added screenshots and proposed code which seems to fix the issue.Registered
@Andrus: OK! What is much more interesting is: why you had the permutation which contains null. I suppose the origin of the problem was earlier when the buggy permutation was saved.Meridional
This occurs if columns are deleted from column definition table used to render jqgrid. Fix in question is not sufficient. If pemutation array length is greater than colModel length, remapColumns increases colModel lenght! After that colModel contains undefined elements. How to check is the permutation valid for colModel and call remapColumns only if permutation is valid or other idea to fix ?Registered
Replacing null check with var cm = $grid.jqGrid('getGridParam', 'colModel')' and check ` && myColumnsState.permutation.length == cm.length` seems to fix thisRegistered
@Andrus: It seems be a good suggestion! So if (isColState) { $this.jqGrid("remapColumns", myColumnsState.permutation, true); } inside of loadComplete should be changed to if (isColState && myColumnsState.permutation.length === cm.length) { $this.jqGrid("remapColumns", myColumnsState.permutation, true); } where cm should be defined as cm = $grid.jqGrid('getGridParam', 'colModel') at the beginning of loadComplete. Is it the solution?Meridional
Yes. Earlier you suggested to use myColumnsState.permutation.length > 0 If zero length colmodel is created this check is still required. This can removed if we can assume that colmodel contais always at least one element.Registered
I had to add a check for cmi.stype=="select" to see if a select elements need populating then i had to use this code if(cmi.stype=="select") { control = $("select#gs_" + $.jgrid.jqID(cmi.name)); } placed just after the control is obtainedTsai
In Apple mobile phone with Safari attemp to it causes QuotaExceededError: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota. How to fix this for mobile safari? In mobile Chrome and in iPad it works. Maybe to turn persisting silently off in mobile Safari.Registered
@Andrus: Sorry, but if you can reproduces the problem you should debug it yourself. For me all looks just so that the local storage of the IPhone is full, or you try to save very large data in the storage.Meridional
Google search about this error shows that mobile safari reports localstorage presence but actually does not allow to use it. Code in answer should not try to save settings in this case. No idea how to implement this for this code. Currently empty page is displayed in mobile safari if this code is used. jqgrid does not appear at all.Registered
@Andrus: You can just detect Safari of iPhone or just include try-catch for the exception and don't use localStorage.Meridional
S
4

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 });
  }
...
},
Sharyl answered 30/4, 2012 at 14:49 Comment(1)
Just had this problem - thanks for saving me a few hours of debugging :)Ane

© 2022 - 2024 — McMap. All rights reserved.