Friday 30 March 2012

Show or Hide jqGrid columns using Eric Hynds multiselect

In the example below I will setup a jqGrid with a custom navbar button in the pager, both top and bottom, that opens a jQuery UI dialog to show or hide columns in the grid. The multiselect plugin I will be using is by Eric Hynds jQuery UI Multiselect Widget.

Setup jqGrid


The HTML:

<html>
    <head>
        <title>My Grid</title>
        <script type="text/javascript"  src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript"  src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js "></script>
<script type="text/javascript"  src="https://raw.github.com/ehynds/jquery-ui-multiselect-widget/1.12/src/jquery.multiselect.min.js "></script>
<script type="text/javascript"  src="/mySite.js "></script>
<link type="text/stylesheet"  src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/ui-lightness/jquery-ui.css "></link>
    </head>
    <body>
        <table id="selector"></table>
        <div id="pager"></div>
        <script type="text/javascript">
            GridBuilder.setupGrid();
        </script>
    </body>
</html>



 The JavaScript:

var GridBuilder = {

myGrid: {};

setupGrid: function() {
        var navGridOptions = { edit: false, add: false, del: false, search: false, refresh: false, cloneToTop: true };

        //setup the grid        
        GridBuilder.myGrid = $("#selector")
            .jqGrid({
                url: 'MyHandler.ashx',
                datatype: 'json',
                mtype: 'POST',
                colNames: [' ', 'First Name', 'Last Name', 'Project Name', 'Email'],
                colModel: [
                    { name: 'Id', index: 'Id', key: true, classes: 'id', align: "center", width: 30, sortable: false, hidedlg: true },
                    { name: 'FirstName', index: 'FirstName', classes: 'firstName', width: 150, hidedlg: true },
                    { name: 'LastName', index: 'LastName', classes: 'surname', width: 150, hidedlg: true },
                    { name: 'ProjectName', index: 'ProjectName', classes: 'projectname', width: 150 },
                    { name: 'EmailAddress', index: 'EmailAddress', classes: 'email', width: 300 },
],
                toppager: true,
                pager: jQuery('#pager'),
                width: 832,
                height: "100%",
                autowidth: false,
                shrinkToFit: false,
                scrollOffset: 20,
                rowNum: 25,
                rowList: [10, 15, 20, 25, 50, 100, 500],
                sortname: 'LastName',
                sortorder: "asc",
                viewrecords: true,
                hoverrows: false,
                imgpath: '/scripts/themes/coffee/images',
                caption: "",
                beforeRequest: GridBuilder.postToGrid, //reloads the grid with post data
beforeSelectRow: function (rowid, e) { return false; } //this disables row being highlighted when clicked
            })
            .jqGrid("navGrid", "#pager", navGridOptions) //add a nav grid to the pager and top pager
            .jqGrid("navButtonAdd", "#pager", GridBuilder.navButtonColumns($("#selector"))) //add column button to pager            
.jqGrid("navButtonAdd", "#pager_toppager", GridBuilder.navButtonColumns($("#selector"))); //add column button to top pager*/    }
}

Points to note:

GridBuilder - I've placed all my methods into this object which is in a file called mySite.js in the root folder.

navGridOptions - This object is used to set the navGrid in the pager. We can set what buttons we would like to display and if it must be closed to the top pager as well (cloneToTop).

hidedlg - This property in the colModel section of the grid determines if the column will be allowed in the show/hide dialog. If set to true so that column won't be in the list of columns to show or hide.

navButtonAdd - I  use this jqGrid method to add a custom button to the pagers that will pop-up a jqUery UI dialog for showing or hiding columns.

Setup column chooser dialog


First we'll setup the default column chooser:

var GridBuilder = {
 navButtonColumns: function (grid) {
         return {
            caption: "Columns",
            onClickButton: function () {
                grid.jqGrid('columnChooser');
            }
        };
    }
}


Now lets customize the multiselect:

var GridBuilder = {
 navButtonColumns: function (grid) {
         return {
            caption: "Columns",
            onClickButton: function () {
                grid.jqGrid('columnChooser', {
msel_opts: { //multiselect options
                        autoOpen: true,
                        header: false,
                        height: "auto",
                        beforeclose: function () { return false; } //keep multiselect drop down open  
                    }
});
            }
        };
    }
}


Points to note:

We are forcing the multiselect drop down to stay open and remain open (autoOpen and beforeClose)

Now let us customize the dialog window:


var GridBuilder = {
 navButtonColumns: function (grid) {
         return {
            caption: "Columns",
            onClickButton: function () {
                grid.jqGrid('columnChooser', {
msel_opts: { //multiselect options
                        autoOpen: true,
                        header: false,
                        height: "auto",
                        beforeclose: function () { return false; } //keep multiselect drop down open  
                    }
},
dlog_opts: { //dialog options
                        modal: false,
                        resizable: false,
                        draggable: false,
                        buttons: [
                            { text: "Ok", click: function () { GridBuilder.changeColView(grid, this); } },
                            { text: "Cancel", click: function () { $(this).dialog("close"); } }
                        ],
                        close: function (event, ui) {
                            $(".ui-multiselect-menu").hide(); //hide drop down options
                        }
                    }
);
}
            }
//force dialog to show again on 2nd click for some reason it is always hidden
                $("." + className + ".ui-dialog").show();
                $("." + className + ".ui-multiselect-menu").show();
};
    }
}


Points to note:

dlog_opts - The moment you add to customize the dialog we lose the "normal" functionality of the column chooser. I have no idea why this happens. So we need to add our own custom buttons and functions to hide columns. If anyone knows why it would be great to know.

modal, resizable, draggable - We don't want a modal dialog and it must not be resizable or draggable

buttons - We add 2 buttons to replace the "normal" ones that have disappear since using dlog_opts. The Ok button calls a function that shows and hides the columns. The Cancel button closes the dialog.

close - When the dialog is closed this event is fired and in it we hide the multiselect options as they seem to remain visible even though the dialog is not.

Now lets look at the show and hide code:


var GridBuilder = {
 changeColView: function (grid, dialogRef) {
        var colModel = grid.jqGrid("getGridParam", "colModel");

        $(".ui-multiselect-checkboxes li input[type=checkbox]").each(function () {
            var colName = colModel[parseInt($(this).val(), 10)].name;
            $(this).attr("checked") ? grid.showCol(colName) : grid.hideCol(colName);
        });        

        $(dialogRef).dialog("close");
    }
}


Points to note:

Here we get all the columns back from the grid (colModel) then we loop through all the muliselect checkboxes and get the column name to show or hide.

Finally this is the full mySite.js file:

var GridBuilder = {
    myGrid: {},

    setupGrid: function () {
        var navGridOptions = { edit: false, add: false, del: false, search: false, refresh: false, cloneToTop: true };

        //setup the grid        
        GridBuilder.myGrid = $("#selector")
            .jqGrid({
                url: 'MyHandler.ashx',
                datatype: 'json',
                mtype: 'POST',
                colNames: [' ', 'First Name', 'Last Name', 'Project Name', 'Email'],
                colModel: [
                    { name: 'Id', index: 'Id', key: true, classes: 'id', align: "center", width: 30, sortable: false, hidedlg: true },
                    { name: 'FirstName', index: 'FirstName', classes: 'firstName', width: 150, hidedlg: true },
                    { name: 'LastName', index: 'LastName', classes: 'surname', width: 150, hidedlg: true },
                    { name: 'ProjectName', index: 'ProjectName', classes: 'projectname', width: 150 },
                    { name: 'EmailAddress', index: 'EmailAddress', classes: 'email', width: 300 },
],
                toppager: true,
                pager: jQuery('#pager'),
                width: 832,
                height: "100%",
                autowidth: false,
                shrinkToFit: false,
                scrollOffset: 20,
                rowNum: 25,
                rowList: [10, 15, 20, 25, 50, 100, 500],
                sortname: 'LastName',
                sortorder: "asc",
                viewrecords: true,
                hoverrows: false,
                imgpath: '/scripts/themes/coffee/images',
                caption: "",
                beforeRequest: GridBuilder.postToGrid, //reloads the grid with post data
                beforeSelectRow: function (rowid, e) { return false; } //this disables row being highlighted when clicked
            })
            .jqGrid("navGrid", "#pager", navGridOptions) //add a nav grid to the pager and top pager
            .jqGrid("navButtonAdd", "#pager", GridBuilder.navButtonColumns($("#selector"))) //add column button to pager            
.jqGrid("navButtonAdd", "#pager_toppager", GridBuilder.navButtonColumns($("#selector"))); //add column button to top pager*/    
    },

    navButtonColumns: function (grid) {
        return {
            caption: "Columns",
            onClickButton: function () {
                grid.jqGrid('columnChooser', {
                    msel_opts: { //multiselect options
                        autoOpen: true,
                        header: false,
                        height: "auto",
                        beforeclose: function () { return false; } //keep multiselect drop down open  
                    },
                    dlog_opts: { //dialog options
                        modal: false,
                        resizable: false,
                        draggable: false,
                        buttons: [
                            { text: "Ok", click: function () { GridBuilder.changeColView(grid, this); } },
                            { text: "Cancel", click: function () { $(this).dialog("close"); } }
                        ],
                        close: function (event, ui) {
                            $(".ui-multiselect-menu").hide(); //hide drop down options
                        }
                    }
                });
                //force dialog to show again on 2nd click for some reason it is always hidden
                $("." + className + ".ui-dialog").show();
                $("." + className + ".ui-multiselect-menu").show();

            }

        }
    },
    changeColView: function (grid, dialogRef) {
        var colModel = grid.jqGrid("getGridParam", "colModel");

        $(".ui-multiselect-checkboxes li input[type=checkbox]").each(function () {
            var colName = colModel[parseInt($(this).val(), 10)].name;
            $(this).attr("checked") ? grid.showCol(colName) : grid.hideCol(colName);
        });

        $(dialogRef).dialog("close");
    }
};

Monday 5 March 2012

JQuery check/uncheck checkbox not working in IE7

I dynamically create a checkbox using JQuery as seen below:
$('').attr('type', 'checkbox').attr('class', 'myclass').attr('checked', true).appendTo(myContainerElement)
The problem is this doesn't work in IE7. The reason being you can't assign a type after the control is created. So I moved the type into the selection as can be seen below and this fixed my IE7 issue.
$('').attr('class', 'myclass').attr('checked', true).appendTo(myContainerElement)