jqueryjqgridmulti-selectfree-jqgridjqgrid-formatter

jqGrid add multi select column filter to a specific column


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


Solution

  • 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/