0

I am implementing server side paging & sorting for jqGrid in MVC 4. I am passing view model object as postData to jqGrid url action method. have a look at grid definition.

var isGridDefined = false;
$(document).ready(function () {
    function DefineGrid(Year, Month) {
        var mygrid = $("#RptUpload");
        mygrid.jqGrid({
            loadonce: false,
            async: false,
            datatype: 'json',
            postData: { bReload: true, Year: Year, Month: Month },
            url: '@Url.Action("DMEUploadDetailsList", "Reports")',
            jsonReader: { repeatitems: false, root: "DataRows" },
            colNames: ['@VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_OrderID',
                        '@VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_CompanyName',
                        '@VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_PatientID',
                        '@VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_PatientName',
                        "@VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_DOB",
                        '@VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_Insurance',
                        "@VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_UploadDate"
            ],
            colModel: [
                { name: 'ReadingID', index: 'ReadingID', width: 55, fixed: true, sorttype: 'integer', align: 'center' },
                {
                    name: 'CompanyName', index: 'CompanyName', align: 'center', width: 200,
                    cellattr: function (rowId, tv, rawObject, cm, rdata) { return 'style="white-space: normal!important;' },
                },
                { name: 'PatientID', index: 'PatientID', width: 55, fixed: true, sorttype: 'integer', align: 'center' },
                {
                    name: 'PatientName', index: 'PatientName', align: 'center', width: 200,
                    cellattr: function (rowId, tv, rawObject, cm, rdata) { return 'style="white-space: normal!important;' },
                },
                {
                    name: 'DOB', index: 'DOB', width: 80, fixed: true, sorttype: 'date', formatter: 'date', formatoptions: { srcformat: 'm/d/Y', newformat: 'm/d/Y' },
                    align: 'center'
                },
                { name: 'InsuranceType', index: 'InsuranceType', align: 'center', width: 150, cellattr: function (rowId, tv, rawObject, cm, rdata) { return 'style="white-space: normal!important;' }, },
                {
                    name: 'UploadDate', index: 'UploadDate', width: 80, fixed: true, sorttype: 'date', formatter: 'date', formatoptions: { srcformat: 'm/d/Y', newformat: 'm/d/Y' },
                    align: 'center'
                }
            ],
            rowNum: 20,
            rowList: [20, 50, 100, 200],
            pager: '#UploadPager',
            caption: '@VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_Title',
            viewrecords: true,
            height: 'auto',
            width: 770,
            hidegrid: false,
            shrinkToFit: true,
            scrollOffset: 0,
            headertitles: true,
            loadError: function (xhr, status, error) {
                alert(status + " " + error);
            },
            //onPaging: function (pgButton) {
            //    $("#RptUpload").jqGrid("setGridParam", { postData: { bReload: false } });
            //},
            loadCompete: function () {
                $("#RptUpload").jqGrid("setGridParam", { datatype: 'json', postData: { bReload: false } });
            }
        });
        mygrid.navGrid('#UploadPager', { edit: false, add: false, del: false, search: false, refresh: false });
        isGridDefined = true;
    }

    $("#rptRefresh").click(function (e) {
        e.preventDefault();
        var Form = $("form[id='FrmDMEUploadDetails']");
        Form.validate();
        if (Form.valid()) {
            RemoveValidatioMessages();
            $("#gridContainer").show();
            var Year = $("#Year").val();
            var Month = $("#Month").val();
            if (!isGridDefined)
                DefineGrid(Year, Month);
            else
                $("#RptUpload").jqGrid("setGridParam", { datatype: "json", page: 1, postData: { bReload: true, Year: Year, Month: Month } }).trigger("reloadGrid");
        }
        else {
            $("#RptUpload").clearGridData();
            $("#gridContainer").hide();
        }
        $(".chzn-select-deselect").trigger("liszt:updated");
        return false;
    });
});

& my action method is as follows

public ActionResult DMEUploadDetailsList(bool bReload, string Year, string Month, string nd, int rows, int page, string sidx, string sord, string filters)
    {
        DataSet SearchResult = null;
        List<ReportData> ResultRows = new List<ReportData>();
        JQGridResult Result = new JQGridResult();
        if (bReload)
        {
            SearchResult = DB.ExecuteDataset("ConnectionString", "pc_GetUploadDetail",
                                            new SqlParameter("@Year", Year),
                                            new SqlParameter("@Month", Month));

            Common.SetSession(SearchResult, null, "DMEUploadByMonth");
        }
        else
            SearchResult = SessionManager.GetSession().GetAttribute("DMEUploadByMonth") as DataSet;

        if (SearchResult != null)
        {
            DataTable dtSearchResult = SearchResult.Tables[0];

            # region Handle server side Filtering, sorting and paging
            int totalRecords = dtSearchResult.Rows.Count; //before paging
            int totalPages = (int)Math.Ceiling((decimal)totalRecords / (decimal)rows); //--- number of pages
            int startIndex = ((page > 0 ? page - 1 : 0) * rows);
            if (sidx != "")
            {
                dtSearchResult.DefaultView.Sort = sidx + " " + sord;
                dtSearchResult = dtSearchResult.DefaultView.ToTable();
            }
            # endregion

            for (int i = startIndex; i < dtSearchResult.Rows.Count; i++)
            {
                ResultRows.Add(new ReportData()
                {
                    ReadingID = Convert.ToInt32(dtSearchResult.Rows[i][0]),
                    CompanyName = Convert.ToString(dtSearchResult.Rows[i][1]),
                    PatientID = Convert.ToInt32(dtSearchResult.Rows[i][2]),
                    PatientName = Convert.ToString(dtSearchResult.Rows[i][3]),
                    DOB = (dtSearchResult.Rows[i][4] != DBNull.Value ? Convert.ToDateTime(dtSearchResult.Rows[i][4]) : (DateTime?)null),
                    InsuranceType = Convert.ToString(dtSearchResult.Rows[i][5]),
                    UploadDate = (dtSearchResult.Rows[i][6] != DBNull.Value ? Convert.ToDateTime(dtSearchResult.Rows[i][6]) : (DateTime?)null)
                });
                if (ResultRows.Count == rows) break;
            }
            Result.DataRows = ResultRows;
            Result.page = page;
            Result.total = totalPages;
            Result.records = totalRecords;
        }
        return Json(Result, JsonRequestBehavior.AllowGet);
    }

The problem with current implementation is that my action method DMEUploadDetailsList is not getting called though view model object is getting passed to request successfuly. & This implementation were working fine when used client side paging & sorting. Please suggest me If I am missing anything or correct my mistakes to get server side paging & sorting working.

This grid is defined or reloaded on refresh button. Now what I want is to identify whether action method is called on refresh button click or paging & sorting operation?

[ Now I would like to describe the last two sentence of problem statement. It specifies that when my page is loaded grid is not defined. As soon as I select filter & clicks refresh button my grid is defined for first time & reloaded for subsequent clicks of refresh. If you go through the action method code you will see that i am trying to use bReload bit variable for, when it is true [in case of refresh button click] I would like to query data from SQL otherwise from dataset stored in session [in case of paging or sorting request]. Now If you looked at the postData parameter in definition or in reload call I am passing breload as true. Where as I am not aware of how can I override this parameter to false when user request for sorting & paging. Or else if there is any another simple way with which in action method I can get whether this request is of load data or paging & sorting.]

Shaggy
  • 315
  • 2
  • 9
  • 23
  • @Oleg Hi Thanks for your reply. I changed my jqGrid definition & action method definition to have separate Year & Month parameter. & it start working; my action method is getting called. I would like to know why it was not working when I was passing these values as class object. – Shaggy Oct 14 '13 at 13:16

2 Answers2

1

Sorry, but I don't see any implementation of paging in your code. You calculate the number of records which need be skipped and save it in startIndex, but you don't use startIndex later. Your current code just get data from DataTable and returns all items of the table. You need to skip startIndex items. For example you can start for loop from i = startIndex instead of i = 0.

In general it would be more effective to construct SELECT statement in SqlCommand which uses TOP construct or use STORED PROCEDURE like described in the answer (see another answer too). In the way your server code will get from the SQL server only one page of data instead of fetching all records of data and returning only one page.

UPDATED: I would rewrite your client code to something like the following

$(document).ready(function () {
    var templateDate = {
            width: 80,
            fixed: true,
            sorttype: "date",
            formatter: "date",
            formatoptions: { srcformat: "m/d/Y", newformat: "m/d/Y" }
        },
        templateInt = { width: 55, fixed: true, sorttype: "integer" },
        templateText = {
            width: 200,
            cellattr: function () {
                return 'style="white-space: normal!important;'
            }
        },
        mygrid = $("#RptUpload");

    // create the grid without filling it (datatype: "local")
    mygrid.jqGrid({
        datatype: "local", // to revent initial loading of the grid
        postData: {
            bReload: true,
            Year: function () { return $("#Year").val(); },
            Month: function () { return $("#Month").val(); }
        },
        url: '@Url.Action("DMEUploadDetailsList", "Reports")',
        jsonReader: { repeatitems: false, root: "DataRows" },
        colNames: [ "@VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_OrderID",
                    "@VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_CompanyName",
                    "@VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_PatientID",
                    "@VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_PatientName",
                    "@VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_DOB",
                    "@VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_Insurance",
                    "@VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_UploadDate"
        ],
        colModel: [
            { name: "ReadingID", template: templateInt },
            { name: "CompanyName", template: templateText },
            { name: "PatientID", template: templateInt },
            { name: "PatientName", template: templateText },
            { name: "DOB", template: templateDate },
            { name: "InsuranceType", width: 150, template: templateText },
            { name: "UploadDate", template: templateDate }
        ],
        cmTemplate: { align: "center" },
        rowNum: 20,
        rowList: [20, 50, 100, 200],
        pager: "#UploadPager",
        caption: "@VirtuOxAdmin.DMEUploadDetails_Grid_RptUpload_Title",
        viewrecords: true,
        height: "auto",
        width: 770,
        hidegrid: false,
        headertitles: true,
        loadError: function (xhr, status, error) {
            alert(status + " " + error);
        }
    });
    mygrid.navGrid("#UploadPager",
        { edit: false, add: false, del: false, search: false, refresh: false });
    mygrid.closest(".ui-jqgrid").hide(); // hide the grid

    $("#rptRefresh").click(function (e) {
        var Form = $("form[id=FrmDMEUploadDetails]");
        e.preventDefault();
        Form.validate();
        if (Form.valid()) {
            RemoveValidatioMessages();
            mygrid.jqGrid("setGridParam", { datatype: "json" })
                .trigger("reloadGrid", [{page: 1}])
                .closest(".ui-jqgrid").show(); // show the grid;
        } else {
            mygrid.clearGridData();
            mygrid.closest(".ui-jqgrid").hide(); // hide the grid
        }
        $(".chzn-select-deselect").trigger("liszt:updated");
        return false;
    });
});

The grid will be created with datatype: "local", so the url and postData will be ignored. After that it seems to me the usage of bReload property in postData and on the server side seems me unneeded. Nevertheless I included bReload still in the JavaScript code till you remove it from the server code.

Additionally I simplified the colModel by usage of column templates (cmTemplate option of jqGrid and template property of colModel). See the old answer for more information. I removed also some unneeded options of jqGrid which values are default (see "Default" column in the documentation of options).

About usage of new versions of STORED PROCEDURE (pc_GetUploadDetail in your code) you can consider to introduction new version (like pc_GetUploadDetailPaged) which supports more parameters. It will not break existing code which uses the old procedure, but you can still use sorting and paging of data on SQL Server instead of getting all query results to web server and implementing sorting and paging in C#.

Community
  • 1
  • 1
Oleg
  • 220,925
  • 34
  • 403
  • 798
  • Sorry, I forgot to update the paging code in problem statement. Well about updating stored procedures I am not allowed to do so as old application still in use. We are upgrading the project technology. So we cant alter the stored procedures in any way as it will affect the application in use. I have updated the problem statement to to include the paging code & elaborated on last two sentences of problem statement. Please have a look. – Shaggy Oct 15 '13 at 05:56
  • @Shaggy: I think that the current problem is on the client side. You don't posted currently how you use `DefineGrid`. The usage of `postData: Form.serialize()` is also unclear: which form you use how you set all values and so on. Typically one use `datatype: "local"` initially in grid to have no loading before the user set the filter. Then is the user click reload grid one can call `setGridParam` to set `datatype: "json"`, set values of the form. Then one calls `.trigger("reloadGrid")`. – Oleg Oct 15 '13 at 06:15
  • Ohh! Sorry again, I just updated server side action method code let me update the grid definition & add script to show how I m using DefineGrid(). & I am not using Form.serialize() to pass values anymore. – Shaggy Oct 15 '13 at 06:53
  • I hope the code updatetd is now clear to you. As you can see loadComplete event I am trying to make bReload postData parameter false so that when user clicks paging or sorting I can get data from session instead of SQL server. But postData parameter bReload is not getting overrded in loadComplete as I m not triggering reload there. Is this wrong way of doing so? – Shaggy Oct 15 '13 at 07:02
  • @Shaggy: I wrote my suggestions in **UPDATED** part of my answer. – Oleg Oct 15 '13 at 09:06
  • Thanks for adding new jqGrid terminology to my knowledge base. If I am taking it right, basically you are suggesting me to have paging & sorting functionality on SQL server; which removes my requirement of evaluating **bReload** value in action method to decide whether to reload from SQL or session. At the end even I think this would be more simplified solution for my problem; but then I need to get confirmation from client for adding new version of stored procedures. – Shaggy Oct 15 '13 at 09:33
  • @Shaggy: You are welcome! The moving sorting and logic to SQL server could be the next step. The modification of JavaScript code which I posted should solve your current problems how I understand there. You don't need make any changes of the server code of to write new STORED PROCEDURE at the beginning. If the code works like expected you can remove unneeded `bReload` parameter becaue it will be always `true`, but the problem of first loading will be still solved. You can write new STORED PROCEDURE later. – Oleg Oct 15 '13 at 09:44
  • I discussed this solution with my team mate; & according to him I may not get permission to add new stored procedure in client DB. In that case can you provide any other alternative for this. For e.g. use of extra parameter like bReload in postData array become mandatory. I would like to have alternate solution in my hand before starting discussion with my client. Thanks – Shaggy Oct 15 '13 at 09:51
0

I solved my initial problem of jqGrid url not getting called by removing Form.serialize() as postData parameter in jqGrid definition & reload calles. This problem was not related to server side sorting & paging. This was caused due to my previous postData parameter definition as Form.serialize(). Instead I used pass individual parameters to the postData array. My next problem was related to sorting & paging at server side. In which I want to fetch data from session dataset when user page through the grid or want to sort the grid data; otherwise reload new data by querying SQL server. According to Oleg answer I must have adopted the simplified way of doing paging & sorting at SQL end instead of in c#. But I was not allowed to add new version of stored procedure as per Oleg suggestion. So I stick to use extra parameter in postData array named operCode like this in grid definition.

 postData: {
            operCode: "Reload",
            Year: function () { return $("#Year").val(); },
            InsuranceID: function () { return $("#InsuranceType").val(); },
            CustomerID: function () { return $("#CompanyName").val(); }
        },

Now I added onPaging & onSortCol event to override operCode postData parameter value like this

onPaging: function (pgButton) {
            mygrid.jqGrid("setGridParam", { datatype: 'json', postData: { operCode: "Paging" } });
        },
        onSortCol: function (index, iCol, sortorder) {
            mygrid.jqGrid("setGridParam", { datatype: 'json', postData: { operCode: "Sorting" } });
        }

Now whenever user clicks refresh button operCode is sent as Reload, on paging as "Paging" & on sorting as "Sorting"

My server side action method code is as follows

public ActionResult DMEUploadDetails(string operCode, string Year, string Month, string nd, int rows, int page, string sidx, string sord, string filters)
    {
        DataSet SearchResult = null;
        List<ReportData> ResultRows = new List<ReportData>();
        JQGridResult Result = new JQGridResult();
        if (operCode == "Reload")
        {
            SearchResult = DB.ExecuteDataset("ConnectionString", "pc_GetUploadDetail",
                                            new SqlParameter("@Year", Year),
                                            new SqlParameter("@Month", Month));

            Common.SetSession(SearchResult, null, "POXMonthlyUploads");
        }
        else
            SearchResult = (SessionManager.GetSession().GetAttribute("POXMonthlyUploads") as System.Web.UI.WebControls.GridView).DataSource as DataSet;

        if (SearchResult != null)
        {
            DataTable dtSearchResult = SearchResult.Tables[0];

            # region Handle server side Filtering, sorting and paging
            int totalRecords = dtSearchResult.Rows.Count; //before paging
            int totalPages = (int)Math.Ceiling((decimal)totalRecords / (decimal)rows); //--- number of pages
            int startIndex = ((page > 0 ? page - 1 : 0) * rows);
            if (sidx != "" && operCode == "Sorting")
            {
                dtSearchResult.DefaultView.Sort = sidx + " " + sord;
                dtSearchResult = dtSearchResult.DefaultView.ToTable();
                SearchResult.Tables.RemoveAt(0);
                SearchResult.Tables.Add(dtSearchResult);
                Common.SetSession(SearchResult, null, "POXMonthlyUploads");
            }
            # endregion

            for (int i = startIndex; i < dtSearchResult.Rows.Count; i++)
            {
                ResultRows.Add(new ReportData()
                {
                   //code to fill data to structure object
                });
                if (ResultRows.Count == rows) break;
            }
            Result.DataRows = ResultRows;
            Result.page = page;
            Result.total = totalPages;
            Result.records = totalRecords;
        }
        return Json(Result, JsonRequestBehavior.AllowGet);
    }

Very much Thanks to Oleg for giving me some extra knowledge about jqGrid & giving me good idea about server side paging & sorting.

Shaggy
  • 315
  • 2
  • 9
  • 23