Сохранение предпочтений столбца jqGrid

У меня есть несколько jqGrids в моем приложении ASP.NET MVC 3, у которых есть несколько столбцов. Я добавил следующие определения столбцов, чтобы по умолчанию были скрыты некоторые столбцы:

colModel: [
   { name: 'IceCreamID', hidden: true},
   { name: 'RecipeID', hidden: true }

и это работает хорошо. Эти столбцы не видны в моей сетке.

Затем я добавил это для реализации выбора столбца:

var grid = $('#icecreamGrid');
grid.jqGrid('navButtonAdd', '#icecreamPager',
{ caption: "Columns", buttonicon: "ui-icon-calculator",
  title: "Choose Columns",
  onClickButton: function() {
     grid.jqGrid('columnChooser');
  }
});

Отлично, теперь поднимает выбор столбца. Затем я добавил следующие столбцы, которые я никогда не хотел отображать в выборе столбца:

colModel: [
   { name: 'IceCreamID', hidden: true, hidedlg: true},

Итак, теперь я могу просто скрыть/показать столбцы. Теперь, как бы вы сохраняли эту информацию? DB? Как печенье? Другой путь? Есть ли предпочтительный способ хранения такой информации, которая действительно является предпочтением пользователя, а не чем-то, связанным с самими данными?


Дополнительная информация

Основываясь на комментарии Олега ниже, я хочу предоставить немного больше информации.

Дело в том, что у меня есть сетки с 10-15 столбцами, которые могут отображаться на основе предпочтений пользователя. Для простого примера одна из моих сетки имеет следующие 9 столбцов:

IceCream|ShortName|HasNuts|SugarAdded|LimitedRun|PromoItem|Facility|FirstRun|LastRun

Пользователи могут скрывать/показывать любой из этих 9 столбцов на основе своих личных предпочтений.

То, что я хочу сделать, - это способ сохранить те столбцы, которые конкретный пользователь хочет увидеть, чтобы он/она не нуждались в повторном выборе этих столбцов для просмотра каждый раз, когда отображается страница с этой сеткой.

Ответ 1

Я нашел вас очень интересным. Вопрос о сохранении пользовательского состояния сетки интересен во многих случаях. Есть несколько интересных ответов на такие проблемы, которые используют cookie (см. здесь).

По моему мнению сохранение состояния сетки в базе данных на сервере или в localStorage является лучшим способом использования cookie. Лучший способ зависит от требований проекта, в которых вы его используете. Например, использование хранилища базы данных на сервере позволяет реализовать состояние роуминга сети. Если вы используете localStorage вместо файлов cookie, пользовательские настройки будут потеряны, если пользователь перейдет на другой компьютер или просто, если пользователь будет использовать другой веб-браузер на том же компьютере.

Другая проблема с состоянием сетки - это обслуживание. Информация о столбцах сетки, которую вы обычно держите в файлах JavaScript или HTML, а не в базе данных. В случае, если оба источника могут быть несинхронными по изменениям в сетке. Различные сценарии проблемы обновления вы могли бы легко представить. Тем не менее преимущества пользовательских предпочтений настолько велики в некоторых сценариях, что проблемы с небольшими недостатками не так важны и могут быть решены относительно легко.

Итак, я потрачу некоторое время на реализацию двух демоверсий, которые показывают, как это можно реализовать. Я использовал localStorage в своих демонстрациях по многим причинам. Я упоминаю только два оттуда:

  • Cookies - это способ, по которому на сервер или с сервера, на который на самом деле не начисляется полная информация. Он увеличивает размер заголовка HTTP и снижает производительность веб-сайта (см. здесь).
  • Cookies имеют очень жесткие ограничения. Соответствует разделу 6.3 rfc2109 или 6.1 of rfc6265: По меньшей мере 4096 байт на файл cookie, по меньшей мере 50 файлов cookie на домен (20 в rfc2109), не менее 3000 файлов cookie всего (300 в rfc2109). Таким образом, файлы cookie, которые нельзя использовать для сохранения слишком большого количества информации. Например, если вы сохраните состояние каждой сетки каждой своей веб-страницы, вы сможете быстро достичь пределов.

С другой стороны localStorage поддерживаются всеми современными браузерами и будут поддерживаться в Internet Explorer, начиная с IE8 (см. здесь), localStorage будет автоматически сохранен для каждого источника (например, a1.example.com, a2.example.com, a3.example.com и т.д.) И имеет произвольный предел в 5 МБ на каждый источник (см. здесь). Поэтому, если вы тщательно используете пространство, вы будете далеко от каких-либо ограничений.

Итак, я использовал в своих демонстрациях localStorage. Я должен дополнительно упомянуть, что есть некоторые плагины, такие как jStorage, которые используют localStorage, если он поддерживается браузером и использует другое хранилище, но тот же интерфейс для вас в случае старых браузеров, таких как IE6/IE7. В случае, если у вас меньше размера хранилища: 128 кбайт вместо 5 МБ, но лучше, чем 4 КБ, для которого есть файлы cookie (см. здесь).

Теперь о реализации. Я создаю две демонстрации: this и расширенная версия: this.

В первая демонстрация сохраняются и автоматически восстанавливаются следующие состояния сетки при перезагрузке страницы (F5 в большинстве веб-сайтов браузеры):

  • какой столбец скрыт
  • порядок столбцов
  • ширина каждого столбца
  • имя столбца, по которому будет сортироваться сетка, и направление сортировки
  • номер текущей страницы
  • текущий фильтр сетки и флаг, применяется ли фильтр. Я использовал параметр multipleSearch: true в сетке.

Таким же образом можно расширить (или уменьшить) список параметров, которые являются частью сохраненного состояния сетки.

Наиболее важные части кода из демонстрации вы найдете ниже:

var $grid = $("#list"),
    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 $.parseJSON(window.localStorage.getItem(storageItemName));
        }
    },
    myColumnStateName = 'ColumnChooserAndLocalStorage.colState',
    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,
                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;
    },
    firstLoad = true;

myColumnsState = restoreColumnState(cm);
isColState = typeof (myColumnsState) !== 'undefined' && myColumnsState !== null;

$grid.jqGrid({
    // ... other 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',
    loadComplete: function () {
        if (firstLoad) {
            firstLoad = false;
            if (isColState) {
                $(this).jqGrid("remapColumns", myColumnsState.permutation, true);
            }
        }
        saveColumnState.call($(this), this.p.remapColumns);
    }
});
$grid.jqGrid('navButtonAdd', '#pager', {
    caption: "",
    buttonicon: "ui-icon-calculator",
    title: "choose columns",
    onClickButton: function () {
        $(this).jqGrid('columnChooser', {
            done: function (perm) {
                if (perm) {
                    this.jqGrid("remapColumns", perm, true);
                    saveColumnState.call(this, perm);
                }
            }
        });
    }
});
$grid.jqGrid('navButtonAdd', '#pager', {
    caption: "",
    buttonicon: "ui-icon-closethick",
    title: "clear saved grid settings",
    onClickButton: function () {
        removeObjectFromLocalStorage(myColumnStateName);
    }
});

Будьте осторожны, чтобы определить myColumnStateName (значение "ColumnChooserAndLocalStorage.colState'``) в демонстрации) для разных значений на разных страницах.

Вторая демонстрация - это расширение первой, использующей технику из моего старого ответа к вашему другому вопросу. Демонстрация использует панель инструментов поиска и синхронизирует дополнительную информацию между расширенной формой поиска и панелью поиска.

ОБНОВЛЕНО: следующий ответ содержит расширенную версию кода, включенную выше. Он показывает, как сохранить выбранные строки (или строку) дополнительно. Еще один ответ показывает, как сохранить список расширенных узлов сетки дерева и развернуть узлы при перекодировании страницы.