jqGrid add multi select column filter to a specific column
Asked Answered
I

1

1

I am trying to add the multi-select filter to my PROVIDER column in the jqGrid. I am able to add the select filter but now I am converting it to the multi-select filter. I referred to a few old posts here and tried to do the same. It's not throwing me any error but it is not creating the multi-select filter also. Please let me know what I am doing wrong below. I am able to get the unique values and able to create the SELECT list, I am guessing something is wrong with function dataInitMultiselect because I tried to console.log(elem) but it's not returning anything, not even undefined but the function is getting called because its not throwing me undefined function error.

$("#home_grid").jqGrid({
        url: "/URL_TO_FETCH_DATA",
        datatype: "json",
        mtype: "GET",
        colNames: ["Provider", "Title","Original Publish Time", "Fetch Time"],
        colModel:
        [
            {
                name    : "PROVIDER",
                align   : "center",
                width   : "120%",
                search  : true
            },
            {
                name    : "TITLE",
                align   : "center",
                search  : true,
                width   : "250%",
                formatter: Title_Url_Bind 
            },
            {
                name        : "PUBLISH_TIME",
                align       : "center",
                width       : "130%",
                search      : true,
                sorttype    : "datetime"

            },
            {
                name        : "DB_ENTRY_TIME",
                align       : "center",
                width       : "130%",
                sortable    : true,
                sorttype    : "datetime"
            }
        ],
        pager       : "#home_pager",
        loadonce    : true,
        shrinkToFit : true,
        rowNum      : 10,
        autoHeight  : true,
        rowList     : [10, 15, 20, 25, 50],
        sortable    : true,
        viewrecords : true,
        toolbar     : [true, "top"],
        autowidth   : true,
        beforeProcessing: beforeProcessingHandler1,
    });

    function beforeProcessingHandler1(data) {
        initializeGridFilterValueDem(data);
    }

    initializeGridFilterValueDem = function (data) {
        setSearchSelect("Provider", jQuery("#home_grid"), data);
    }

    setSearchSelect = function (columnName, grid,data) {
        grid.jqGrid('setColProp', columnName,
            {
                searchoptions: {
                    clearSearch: false,
                    sopt: ['eq', 'ne'],
                    value: buildSearchSelect(getUniqueNames(columnName, data,grid)),
                    attr: { multiple: 'multiple', size: 7},
                    dataInit: dataInitMultiselect
                }
            }
        );
    }

    buildSearchSelect = function (uniqueNames) {
        var values = "";
        $.each(uniqueNames, function () {
            values += this + ":" + this + ";";
        });
        return values.substring(0, values.length - 1);
    }

    getUniqueNames = function (columnName, mydata_parm, grid) {

        var mydata = grid.jqGrid("getGridParam", "data");

        var texts = $.map(mydata, function (item) {
            return item[columnName];
        }),

        uniqueTexts = [], textsLength = texts.length, text, textsMap = {}, i;

        for (i = 0; i < textsLength; i++) {
            text = texts[i];
            if (text !== undefined && textsMap[text] === undefined) {
                // to test whether the texts is unique we place it in the map.
                textsMap[text] = true;
                uniqueTexts.push(text);
            }
        }

        return uniqueTexts;
    }

    dataInitMultiselect = function (elem) {
        console.log(elem);
        setTimeout(function () {
            var $elem = $(elem), id = elem.id,
                inToolbar = typeof id === "string" && id.substr(0, 3) === "gs_",
                options = {
                    selectedList: 2,
                    height: "auto",
                    checkAllText: "all",
                    uncheckAllText: "no",
                    noneSelectedText: "Any",
                    open: function () {
                        var $menu = $(".ui-multiselect-menu:visible");
                        $menu.width("auto");
                        return;
                    }
                },
                $options = $elem.find("option");
            if ($options.length > 0 && $options[0].selected) {
                $options[0].selected = false; // unselect the first selected option
            }
            if (inToolbar) {
                options.minWidth = 'auto';
            }
            //$elem.multiselect(options);
            $elem.multiselect(options).multiselectfilter({ placeholder: '' });
            $elem.siblings('button.ui-multiselect').css({
                width: inToolbar ? "98%" : "100%",
                marginTop: "1px",
                marginBottom: "1px",
                paddingTop: "3px"
            });
        }, 50);

    };

Thanks a lot for the response, Yes I am using the free jqGrid. As per the mentioned comment I tried to change the code but still not working for me. Please find below the updated code, I tried to do as mentioned in the jqGrid MultiSelect Demo

But it's throwing me the error Uncaught ReferenceError: multiselectTemplate is not defined, Please let me know how to resolve this. Since they have used the local data to load the jqgrid I am finding it difficult to resolve this issue.

    //FUNCTION TO POPULATE THE TABLE WITH THE DATA
function Grid_Table_Populator()
{
    //Populdate the Datatable with the WEB Feed data
    $("#home_grid").jqGrid({
        url: "/Web_Feed_Data",
        datatype: "json",
        mtype: "GET",
        colNames: ["ID", "PROVIDER", "Title"],
        colModel:
        [
            {
                name    : "ID",
                align   : "center",
                search  : true,
                hidden  : true
            },
            {
                name    : "PROVIDER",
                align   : "center",
                width   : "120%",
                type    : "text", 
                search  : true, 
                template: multiselectTemplate       
            },
            {
                name    : "TITLE",
                align   : "center",
                search  : true,
                width   : "250%",
                formatter: Title_Url_Bind 
            },
        ],
        pager       : "#home_pager",
        loadonce    : true,
        shrinkToFit : true,
        rowNum      : 10,
        autoHeight  : true,
        rowList     : [10, 15, 20, 25, 50],
        sortable    : true,
        sortname    : "TITLE",
        sortorder   : "desc",
        viewrecords : true,
        toolbar     : [true, "top"],
        autowidth   : true,
        caption     : 'Table Data',
        loadComplete: function(data)
        {
            if (!this.ftoolbar) {
                // create filter toolbar if it isn't exist 
                $(this).jqGrid("filterToolbar", {       
                    defaultSearch: "cn",
                    beforeClear: function() {
                    $(this.grid.hDiv)
                        .find(".ui-search-toolbar button.ui-multiselect")
                        .each(function() {
                            $(this).prev("select[multiple]").multiselect("refresh");
                        });
                    }
                });
                $(this).triggerHandler("jqGridRefreshFilterValues");
                $(this.grid.hDiv)
                    .find(".ui-search-toolbar button.ui-multiselect")
                    .each(function() {
                    $(this).prev("select[multiple]")
                        .multiselect("refresh");
                });        
            }
        },
    }); 

    dataInitMultiselect = function (elem, searchOptions) {
        var $grid = $(this);
            setTimeout(function() {
                var $elem = $(elem),
                    id = elem.id,
                    inToolbar = searchOptions.mode === "filter",
                    options = {
                        selectedList: 2,
                        height: "auto",
                        checkAllText: "all",
                        uncheckAllText: "no",
                        noneSelectedText: "Any",
                        open: function() {
                            var $menu = $(".ui-multiselect-menu:visible");
                            $menu.width("auto");
                            var height = $menu.find(">ul>li").first().outerHeight();
                            $menu.find(">ul").css("maxHeight", 5 * Math.max(height, 12));
                            return;
                        }
                    },
                    $options = $elem.find("option");
                if ($options.length > 0 && $options[0].selected) {
                    $options[0].selected = false; // unselect the first selected option

                }
                if (inToolbar) {
                    options.minWidth = "auto";
                }
                $grid.triggerHandler("jqGridRefreshFilterValues");
                $elem.multiselect(options);
                // replace icons ui-icon-check, ui-icon-closethick, ui-icon-circle-close
                // and ui-icon-triangle-1-s to font awesome icons
                var $header = $elem.data("echMultiselect").header;
                $header.find("span.ui-icon.ui-icon-check")
                    .removeClass("ui-icon ui-icon-check")
                    .addClass("fa fa-fw fa-check");
                $header.find("span.ui-icon.ui-icon-closethick")
                    .removeClass("ui-icon ui-icon-closethick")
                    .addClass("fa fa-fw fa-times");
                $header.find("span.ui-icon.ui-icon-circle-close")
                    .removeClass("ui-icon ui-icon-circle-close")
                    .addClass("fa fa-times-circle");
                $elem.data("echMultiselect")
                    .button
                    .find("span.ui-icon.ui-icon-triangle-1-s")
                    .removeClass("ui-icon ui-icon-triangle-1-s")
                    .addClass("fa fa-caret-down")
                    .css({
                        float: "right",
                        marginRight: "5px"
                    });

                $elem.siblings("button.ui-multiselect").css({
                    width: "100%",
                    margin: "1px 0",
                    paddingTop: ".3em",
                    paddingBottom: ".3em"
                });
            }, 50);
        },    
        multiselectTemplate = {
            stype: "select", 
            searchoptions: {
                generateValue: true,
                //noFilterText: "Any",
                sopt: ["in"],
                attr: {
                    multiple: "multiple",
                    size: 3
                },
                dataInit: dataInitMultiselect
            }
    };
}

I tried declaring it within the function and outside the function but still no luck. Please help me with this issue.

After the lot of help from Oleg I was able to get the Multi-Select but its not working. when I click on it does not expand and show the option. I have posted my code here MY JQGRID CODE@Oleg can you please look at this and provide me with solution.

My jqGrid is looking something like this: JQGRID COLUMMN

Impropriety answered 10/6, 2019 at 14:34 Comment(3)
I tried it again and seems like it's not throwing any error in the other method also. really confused, not sure whats wrong.Impropriety
You ca switch to Guriddo jqGrid and see this exampleHeather
@TonyTomov Thanks a lot for the response. What's the difference between jqGrid which I am using and the Guriddo jqGrid which you have mentioned? Do I need to import any of the CSN links to support this multi-select?Impropriety
A
1

From your other questions one can see that you use free jqGrid fork. It supports generating of unique values automatically. Thus one can use

searchoptions: {
    generateValue: true,
    sopt: ["in"],
    attr: { multiple: "multiple", size: 7 },
    dataInit: dataInitMultiselect
}

instead of

searchoptions: {
    clearSearch: false,
    sopt: ['eq', 'ne'],
    value: buildSearchSelect(getUniqueNames(columnName, data,grid)),
    attr: { multiple: 'multiple', size: 7},
    dataInit: dataInitMultiselect
}

It's important that unique values of data in the column will be filled only after loading the data. Then one should create or recreate filterToolbar after loading the data from the server. One can test this.ftoolbar inside of loadComplete for example to detect whether filterToolbar already exist:

loadComplete: function () {
    if (!this.ftoolbar) {
        // create filter toolbar if it isn't exist 
        $(this).jqGrid("filterToolbar", {
            defaultSearch: "cn",
            beforeClear: function() {
                $(this.grid.hDiv)
                    .find(".ui-search-toolbar button.ui-multiselect")
                    .each(function() {
                    $(this).prev("select[multiple]").multiselect("refresh");
                });
            }
        });
        $(this).triggerHandler("jqGridRefreshFilterValues");
        $(this.grid.hDiv)
            .find(".ui-search-toolbar button.ui-multiselect")
            .each(function() {
            $(this).prev("select[multiple]")
                .multiselect("refresh");
        });        
    }
}

The demo https://jsfiddle.net/OlegKi/ty4e68pm/2/ shows a possible implementation of usage multiselect in free jqGrid. The function dataInitMultiselect has the following implementation:

var dataInitMultiselect = function (elem, searchOptions) {
        var $grid = $(this);
        setTimeout(function() {
            var $elem = $(elem),
                id = elem.id,
                inToolbar = searchOptions.mode === "filter",
                options = {
                    selectedList: 2,
                    height: "auto",
                    checkAllText: "all",
                    uncheckAllText: "no",
                    noneSelectedText: "Any",
                    open: function() {
                        var $menu = $(".ui-multiselect-menu:visible");
                        $menu.width("auto");
                    }
                },
                $options = $elem.find("option");
            if ($options.length > 0 && $options[0].selected) {
                $options[0].selected = false; // unselect the first selected option

            }
            if (inToolbar) {
                options.minWidth = "auto";
            }
            $grid.triggerHandler("jqGridRefreshFilterValues");
            $elem.multiselect(options);
            // replace icons ui-icon-check, ui-icon-closethick, ui-icon-circle-close
            // and ui-icon-triangle-1-s to font awesome icons
            var $header = $elem.data("echMultiselect").header;
            $header.find("span.ui-icon.ui-icon-check")
                .removeClass("ui-icon ui-icon-check")
                .addClass("fa fa-fw fa-check");
            $header.find("span.ui-icon.ui-icon-closethick")
                .removeClass("ui-icon ui-icon-closethick")
                .addClass("fa fa-fw fa-times");
            $header.find("span.ui-icon.ui-icon-circle-close")
                .removeClass("ui-icon ui-icon-circle-close")
                .addClass("fa fa-times-circle");
            $elem.data("echMultiselect")
                .button
                .find("span.ui-icon.ui-icon-triangle-1-s")
                .removeClass("ui-icon ui-icon-triangle-1-s")
                .addClass("fa fa-caret-down")
                .css({
                    float: "right",
                    marginRight: "5px"
                });

            $elem.siblings("button.ui-multiselect").css({
                width: "100%",
                margin: "1px 0",
                paddingTop: ".3em",
                paddingBottom: ".3em"
            });
        }, 50);
    };

UPDATED: I analysed your demo https://jsfiddle.net/B_AV_B/7ecrmtz4/5/. It contains a lot of error:

  1. you missing stype : "select" in multiselect column. The searching field have to have select type (stype : "select") to be able to be displayed as <select> alement, which can be converted later with respect of multiselect control
  2. I wrote you multiple times about importance inserting only one version of jQuery and other JavaScript libraries. Moreover, it's important to hold the order of inserted JS files based on dependencies. Multiselect widget is plugin to jQuery UI. Thus jQuery UI must be inserted before. In short you should replace
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-ui-multiselect-widget/2.0.2/jquery.multiselect.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>   

to

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-ui-multiselect-widget/2.0.2/jquery.multiselect.js"></script>
  1. You should remove ../bootstrap-multiselect/0.9.13/js/bootstrap-multiselect.js, which overwrite multiselect registered previously.
  2. JavaScript is case sensitive. It seems that ID property of input data specifies unique id of every input item. jqGrid uses id instead of ID by default. If you use datatype: "local" then you should include localReader: { id: "ID" } parameter. If you use datatype: "json" then you should include jsonReader: { id: "ID" }. In your case you can include both parameters.
  3. You used wrong code of open callback (compare your code, with the code from my answer). It's enough to use it as var $menu = $(".ui-multiselect-menu:visible"); $menu.width("auto"); without additional actions, which makes some other items invisible.
  4. Values of width property on colModel should be numbers and not strings like "120%". Numbers will be interpreted as pixels. If you use autowidth : true the initial width values will be proportionally increased to make the width of the grid equal to the width of outer element.
  5. Finally I added some CSS rules to your demo
.ui-multiselect-menu .ui-multiselect-header ul,
.ui-multiselect-menu .ui-multiselect-checkboxes li {
    font-size: 12px;
}

.ui-multiselect-menu .ui-multiselect-header a:hover {
    text-decoration: none;
}
.ui-multiselect-menu .ui-multiselect-close {
    margin-right: 3px;
}

You can modify the font-size on the above rule corresponds to your requirements.

Modified demo is https://jsfiddle.net/OlegKi/teLja6z3/25/

Alb answered 16/6, 2019 at 17:45 Comment(17)
Thanks a lot for the response, I have updated the code it's throwing me error can you please check and let me know what mistake I am making.Impropriety
@BAVB: Sorry, but I can't follow what you do. I posed you the demo, which you can modify to reproduce your problem. The code fragment, which you posted is clear wrong. What you mean with dataInitMultiselect = function (elem, searchOptions) {..}? You have to use var before definition of new variable. It's better to define variables before you use it and not after the usage. You should just move declaration of object multiselectTemplate and function dataInitMultiselect before the usage in $("#home_grid").jqGrid({...});Alb
Thanks a lot, I moved it above and its able read it but its throwing me one errror in the line var $header = $elem.data("echMultiselect").header; it says Uncaught TypeError: Cannot read property 'header' of undefined Can you please help me this issue? Thank you :)Impropriety
@BAVB: You should verify which multiselect plugin you use and which version of jQuery UI you use. Compare the versions with my demo jsfiddle.net/OlegKi/ty4e68pm/2. In general, you can set breakpoint (debugger;) in your code before the line var $header = $elem.data("echMultiselect").header; and start your code with opened Developer Tools of chrome. You can display $.data($elem[0]) in watch windows and to see the name of property. In the version, which I use it's echMultiselect. Thus I use $elem.data("echMultiselect"). If you use another version, you need to fix the name.Alb
Thanks a lot and sorry for bugging you again and again but in the demo, I don't see the jQuery and multi-select plugin which you have used. Also i tried to console log and now changed to multiselect instead of echMultiselect but now its throwing me error for $header.find("span.ui-icon.ui-icon-check") and it says Uncaught TypeError: Cannot read property 'find' of undefined Can you please let me know which version you have used so that I can rectify them as in demo I cant find anything in HTML page other than declared table.Impropriety
JSFiddle has "Resources" link on the left side of the demo (see "Resources URL cdnjs 8"). If you click on the "Resources" the resources will be used. With respect of context menu over a resource one can copy the URL to the resource. You will see that the demo uses version 2.0.2 of jquery-ui-multiselect-widget: cdnjs.cloudflare.com/ajax/libs/jquery-ui-multiselect-widget/…, cdnjs.cloudflare.com/ajax/libs/jquery-ui-multiselect-widget/… and so on.Alb
In general one can remove the block, which starts with var $header = $elem.data("echMultiselect").header; and ends with $elem.data("echMultiselect").... The code just modifies jquery-ui-multiselect-widget control to use some Font Awesome icons instead of jQuery UI icons used by defaultAlb
Thanks for the help again still i am not able to get the Multi-Select for my column. I have posted my code here: jsfiddle.net/B_AV_B/7ecrmtz4/5 Can you please let me know what mistake I am making here.Impropriety
Let us continue this discussion in chat.Impropriety
@BAVB: It's difficult to help you with the demo jsfiddle.net/B_AV_B/7ecrmtz4/5 because it's not working, First of all you use url: "/Web_Feed_Data", instead of usage url: "/echo/json/" in my demo. Echo service of jsfiddle allows to send (to post) to the URL some data, which will be returned back. In the way one can test code, which require loading data from the server. Seconds you included a lot of CSS and JS files inside of HTML and you repeat old error, which I mentioned in my answer: you used the same files in different versions, which is wrong.Alb
@BAVB: For example, you included fist slim version of jQuery jquery-3.3.1.slim.min.js (why?), then version 2.0.2 of jquery.multiselect.js. It's unclear, whether jquery.multiselect.js work at all with slim version of jQuery. Then you includes full version 3.3.1 of jQuery, which destroy or at least overwrite slim version of jQuery. Moreover multiselect require jQuery UI JS, which you don't include at all. Instead of that you included jQuery UI CSS of two different versions (!!!) - 1.11.4 and then 1.12.1. It's wrong too.Alb
@BAVB: If you click on "Resources" link on the left panel of my demo you will see that the first JS file, which I use is full version of jQuery (../3.3.1/jquery.min.js), then /4.15.5/jquery.jqgrid.min.js and /1.12.1/jquery-ui.min.js. The file ../jquery-ui-multiselect-widget/2.0.2/jquery.multiselect.js come after jquery-ui.min.js.Alb
I have changed my code and trying to load now local data and it looks like this: jsfiddle.net/B_AV_B/7ecrmtz4/22 So after your explanation, I have changed my data and my CDN links. When you click on the Multi-Select filter I am getting only ALL or NO option I am not able to get any other option to check based on my choice. Can you please look at this once and correct me what I am doing wrong here. Also, I am getting this error in my browser Uncaught TypeError: Cannot read property 'button' of undefinedImpropriety
@BAVB: I posted UPDATED part to my answer, where I described problems (or errors) in your demos. See jsfiddle.net/OlegKi/teLja6z3/25 and jsfiddle.net/OlegKi/83fzL1ns/5Alb
Thanks a lot, @Alb for your patience and response, It is working now. You made my day thanks again. I was missing the stype : "select", and also i was using the jQuery("#home_grid").jqGrid('filterToolbar' to make it worse. I have 1 last question the filter does not refresh automatically based on the data. The filter updates only when I refresh the page again. The table is getting refreshed with new data using the AJAX call but the filter still shows old data and not the updated one. How can I make it refresh automatically?Impropriety
@BAVB: You are welcome! I'm not sure that I correctly understand your current problem. I suppose that you need to trigger event jqGridRefreshFilterValues after the data of the grid will be refreshed or even more easy and safe you can call $(this).jqGrid("destroyFilterToolbar") and then `$(this).jqGrid("filterToolbar", ...); to recreate the filter toolbar and multiselect control based on reloaded data. I'm not sure how exactly the data of grid will be reloaded, but you should recreate the filter after getting and processing the data.Alb
I understood what I was doing wrong. I was reloading the data using the function function clear_search(){ $("#home_grid").jqGrid('setGridParam', { datatype: 'json' }).trigger('reloadGrid'); var $home_grid = $("#home_grid"); if ($home_grid[0].ftoolbar) { $home_grid[0].clearToolbar(); } } but I need to comment last 4 lines of the function so that the new data can be loaded to multi-select. Thanks a lot, You have earned my Bounty point :) You do deserve it truly :)Impropriety

© 2022 - 2024 — McMap. All rights reserved.