2

I have a jqgrid containing some data to filter. I'd like to define a combo box with some pre-defined filter sets / templates. If a user selects an item of the combobox, the grid should automatically apply combined filters. Preferably, the combo box should be integrated into a toolbar or jqGrid's pager, but also in the html page would be fine.

For example:

         COMBO BOX
       Item templates       filter parameters
        ___________
       |Expired    |        << Timeout = true
       |Last Week  |        << OpenDate between 02/13/2012 and 02/16/2012
       |Last Month |        << OpenDate between 01/01/2012 and 02/16/2012
       |.........  |        ......

Thanks in advance for your help

Larry
  • 573
  • 5
  • 14
  • 31
  • 1
    It's very good question! There are templates in the Advanced Searching (see the demo on [the official demo page](http://trirand.com/blog/jqgrid/jqgrid.html) under "Searching"/ "Search Templates"), but there are no support for templates for the filter toolbar. I have an idea how one can implement the searching templates in the filter toolbar. I will post my answer later. – Oleg Feb 16 '12 at 09:53

1 Answers1

4

jqGrid supports Searching Templates in the Advance Searching (see "Searching"/ "Search Templates" in the official jqGrid demo), but there are currently no searching templates support in the Toolbar Filtering.

I find your question very interesting. In the old question I described how one can use generic external filters to send additional information to the server. The way can be good in case of remote data, but it can't be used directly in the local grid or in the grid with the loadonce: true option.

So I created the demo which shows how the filter templates can be implemented in Toolbar Filtering and how to integrated the template in the jqGrid. I used toolbar: [true, "top"] to have additional empty toolbar above the column headers:

enter image description here

In the implementation I used the refreshSerchingToolbar function which I suggested originally here. It's important to understand, that the refreshSerchingToolbar function fill in the filter toolbar only the information which can be exactly represented by the filter. For example the filter for "Closed" rows can be represented in the filter toolbar (see the picture above), but the interval of dates "Last Week" and "Last Month" con't. In the cases the data in the grid will be filtered, but the corresponding fields of the filter toolbar stay empty.

The most important part of the code of the demo you can find below

var $grid = $("#list"),
    initDate = function (elem) {
        $(elem).datepicker({
            dateFormat: 'dd-M-yy',
            autoSize: true,
            changeYear: true,
            changeMonth: true,
            showButtonPanel: true,
            showWeek: true
        });
    },
    numberTemplate = {formatter: 'number', align: 'right', sorttype: 'number', editable: true/*,
        searchoptions: { sopt: ['eq', 'ne', 'lt', 'le', 'gt', 'ge', 'nu', 'nn', 'in', 'ni'] }*/},
    dateTemplate = {width: 80, align: 'center', sorttype: 'date',
            formatter: 'date', formatoptions: { newformat: 'd-M-Y' }, editable: true, datefmt: 'd-M-Y',
            editoptions: { dataInit: initDate },
            searchoptions: { sopt: ['eq', 'ne', 'lt', 'le', 'gt', 'ge'], dataInit: initDate }},
    yesNoTemplate = {align: 'center', editable: true, formatter: 'checkbox',
            edittype: 'checkbox', editoptions: {value: 'Yes:No', defaultValue: 'No'},
            stype: 'select', searchoptions: { sopt: ['eq', 'ne'], value: ':Any;true:Yes;false:No' }},
    myDefaultSearch = 'cn',
    getColumnIndex = function (columnIndex) {
        var cm = this.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 (myDefaultSearch) {
        var filters, i, l, rules, rule, iCol, cmi, control, tagName,
            $this = $(this),
            postData = $this.jqGrid('getGridParam', 'postData'),
            cm = $this.jqGrid('getGridParam', 'colModel');

        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 (this.ftoolbar) === "boolean" && this.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.call($this, 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);
                            }
                        }
                    }
                }
            }
        }
    },
    templateClosed = {
        groupOp: "AND",
        rules: [
            { field: "closed", op: "eq", data: "true" }
        ]
    },
    templateLastWeek = {
        groupOp: "AND",
        rules: [
            { field: "invdate", op: "ge", "data": "13-Feb-2012" },
            { field: "invdate", op: "le", "data": "16-Feb-2012"}
        ]
    },
    templateLastMonth = {
        groupOp: "AND",
        rules: [
            { field: "invdate", op: "ge", "data": "16-Jan-2012" },
            { field: "invdate", op: "le", "data": "16-Feb-2012"}
        ]
    },
    myFilterTemplateLabel = 'Filter by Template:&nbsp;',
    myFilterTemplateNames = ['Closed', 'Last Week', 'Last Month'],
    myFilterTemplates = [templateClosed, templateLastWeek, templateLastMonth],
    iTemplate,
    cTemplates = myFilterTemplateNames.length,
    templateOptions = '',
    reloadWithNewFilterTemplate = function () {
        var iTemplate = parseInt($('#filterTemplates').val(), 10),
            postData = $grid.jqGrid('getGridParam', 'postData');
        if (isNaN(iTemplate)) {
            $grid.jqGrid('setGridParam', {search: false});
        } else if (iTemplate >= 0) {
            $.extend(postData, {
                filters: JSON.stringify(myFilterTemplates[iTemplate])
            });
            $grid.jqGrid('setGridParam', {search: true});
        }
        $grid.trigger('reloadGrid', [{current: true, page: 1}]);
    };

$grid.jqGrid({
    ...
    toolbar: [true, "top"],
    loadComplete: function () {
        var $this = $(this);

        if (typeof (this.ftoolbar) !== "boolean") {
            // create toolbar if needed
            $this.jqGrid('filterToolbar',
                {stringResult: true, searchOnEnter: true, defaultSearch: myDefaultSearch});
        }
        refreshSerchingToolbar.call(this, myDefaultSearch);
    }
});
$.extend($.jgrid.search, {
    multipleSearch: true,
    multipleGroup: true,
    recreateFilter: true,
    closeOnEscape: true,
    closeAfterSearch: true,
    overlay: 0,
    tmplLabel: myFilterTemplateLabel,
    tmplNames: myFilterTemplateNames,
    tmplFilters: myFilterTemplates
});
$grid.jqGrid('navGrid', '#pager', {edit: false, add: false, del: false});
for (iTemplate = 0; iTemplate < cTemplates; iTemplate++) {
    templateOptions += '<option value="' + iTemplate + '">' +
        myFilterTemplateNames[iTemplate] + '</option>';
}
$('#t_' + $.jgrid.jqID($grid[0].id)).append('<label for="filterTemplates">'+
    myFilterTemplateLabel + '</label>' +
    '<select id="filterTemplates"><option value="">Not filtered</option>' +
    templateOptions + '</select>');
$('#filterTemplates').change(reloadWithNewFilterTemplate).keyup(function (e) {
    // some web browsers like Google Chrome don't fire "change" event
    // if the select will be "scrolled" by keybord. Moreover some browsers
    // like Internet Explorer don't change the select option on pressing
    // of LEFT or RIGHT key. Another web browsers like Google Chrome do this.
    // We make refrech of the grid in any from the cases. If needed one
    // could modify the code to reduce unnneded reloading of the grid,
    // but for the demo with a few local rows it's such optimization
    // isn't really needed
    var keyCode = e.keyCode || e.which;

    if (keyCode === $.ui.keyCode.PAGE_UP || keyCode === $.ui.keyCode.PAGE_DOWN ||
            keyCode === $.ui.keyCode.END || keyCode === $.ui.keyCode.HOME ||
            keyCode === $.ui.keyCode.UP || keyCode === $.ui.keyCode.DOWN ||
            keyCode === $.ui.keyCode.LEFT || keyCode === $.ui.keyCode.RIGHT) {

        reloadWithNewFilterTemplate();
    }
});
Community
  • 1
  • 1
Oleg
  • 220,925
  • 34
  • 403
  • 798
  • @Oleg: In demo I entered 400 to amount column in search toolbar and pressed enter to search. After that selected "Last week". 400 was cleared in toolbar and all amounts are shown. How to apply search template in addition to toolbar filter, keep toolbar filter condition also? – Andrus Feb 18 '12 at 10:54
  • 1
    @Andrus: The implementation of the filter templates from [the answer](http://stackoverflow.com/a/9310762/315935) just *set* the `postData.filters`. Any entering of the new value of the filter toolbar *overwrite* the `postData.filters`. What you probably will is to *combining* the current filters from the filter toolbar with the filters from the the filter template chosen. One can implement it using `beforeSearch`. See [the answer](http://stackoverflow.com/a/8953934/315935) which shows the main idea. – Oleg Feb 18 '12 at 11:00
  • @Oleg: this requires filters modification in reloadWithNewFilterTemplate and in beforeSearch, lot of code. I ended up passing separate parameter to controller in reloadWithNewFilterTemplate `$.extend(postData, { filterTemplate: $('#filterTemplates').val() });` This is ugly, where to find code to add AND clause to search? – Andrus Feb 18 '12 at 17:09
  • @Oleg: if browser refresh button is pressed, selected search template is not restored. How to persist it? – Andrus Feb 18 '12 at 17:10
  • @Andrus: I referenced [the answer](http://stackoverflow.com/a/8953934/315935) because it created additional group from the simple filters. My idea was to apply "AND" operation between the current filters from the Filter Toolbar and the filter template. One can do such modification of the `postData.filters` inside of `beforeSearch`. – Oleg Feb 18 '12 at 17:46
  • @Andrus: If you want to persist the filter template, but clear the filter toolbar you should replace the standard Refresh button with the custom one which looks like "Refresh", but implement the behavior. The problem is that what you want is **not** the template for the filter toolbar. I agree that the behavior which you want can be also very helpful, but it is just *another* behavior as described in my current answer. – Oleg Feb 18 '12 at 17:51
  • @Oleg: I asked about browser refresh button, eq. adding class='persistent' to search template select element and using jQuery selector/event to save/restore select element in local storage. – Andrus Feb 18 '12 at 18:17
  • Does this technique only work for local data? I'm thinking it does because how would the server based query know what the filtered fields are? :) – Joe Simes Jun 12 '12 at 18:32
  • @JoeSimes: It should work for both local and remote data. You should just implement advanced searching on the server side. – Oleg Jun 12 '12 at 18:44