Article of the Day! jQGrid and MVC 3 with Custom Paging and Custom Search Filters

I am quite pleased to announce that on 25th April 2012, my article jQGrid and MVC 3 with Custom Paging and Custom Search Filters was selected as Article of the Day on forums.asp.net. For details please visit:-


http://www.asp.net/community/articles/


I am among top 125 contributors around the world in forums.asp.net

I am quite pleased to share that I have received All-Star rank in forums.asp.net. See this:-

It was really wonderful experience while posting in forums. It helped me a lot in gaining in-depth knowledge of ASP .Net technologies. I recommend all other developers to participate in such activity.

You can also checkout my profile at forums.asp.net.

Happy coding !!!

jQGrid and MVC 3 with Custom Paging and Custom Search Filters

During past some days I was studying MVC 3. In start I found MVC little bit challenging. But its features are really exiting, I believe MVC has the power of ASP .Net along with the flexibility of classic ASP.

While implementing data driven application in MVC you have three major choices for grids i.e. MVC DataGrid, jQGrid and MvcContrib grid. jQGrid is really something which has worked best for me. So, I am sharing a sample with you to highlight key features of jQGrid along with MVC.

First of all you have to download jQGrid from jQGrid Home Page. Also you can download the source code for this article.

Client Side Code

First you have to place some container divs where your grid will be rendered like this:-
<div id="content-grid">
    <table id="jq-grid" class="scroll" cellpadding="0" cellspacing="0">
    table>
    <div id="jq-grid-pager" class="scroll" style="text-aligncenter;">
    <div>
<div>
In jq-grid table, your grid will be rendered, while jq-grid-pager will be used for the pager in the grid.

Now you need to initate jQGrid with call to$("#jq-grid").jqGrid({ in your document.ready event. Here I am giving you a description of individual parameters which I used in my sample.

Url is used to point to the location from where grid will fetch the data.
url: 'http://localhost:60760/Home/GetList',//Service URL to fetch data

I generally uses JSON because it is really light weight
datatype: 'json',//Data Type which service will return

Now because I am using custom filters to search the records for my grid. So we have to explicitly pass them to server. They will be passed as JSON string in filters parameter or our server side function which I will discuss later.
postData://Data that will be posted with every request
{
filters: function () {
      return $.toJSON([
                          //Here add the parameters which you like to pass to server
                          { Key: "EmployeeName", Value: $("#txtEmployeeName").val() },
                          { Key: "Id", Value: $("#txtId").val() }
                    ]);
                     }
}

Now we define column and their properties through above code. I always like to fix the widths of columns, however you can ignore width parameter as well. Also note that in last column I disabled sorting using sortable:false.
colNames: ['Employee Id''Employee Name''Action'],//Column Names
colModel: [//Column details
           { name: "Employee Id", index: "Employee Id", width: "220px" },
           { name: "Employee Name", index: "Employee Name", width: "220px" },
           //Do not allow sorting on Action Column
           { name: "Action", index: "Action", sortable: false, width: "220px" }
          ],
I am persisting sort order, sort column, total pages and current page in session, so whenever page is reloaded, I am populating them  from my ViewBag.
sortname: '@ViewBag.EmployeeGridRequest.sidx',//This will persist sorting column 
sortorder: '@ViewBag.EmployeeGridRequest.sord'// This will persist sorting order 
rowNum: @ViewBag.EmployeeGridRequest.rows, //Total Pages
page: @ViewBag.EmployeeGridRequest.page, // Current Page
rowList: [@ViewBag.EmployeeGridRequest.rowList], // Page Selection Options

This one is simple, it will set the grid's caption, if you leave this blank then grid's header will be hidden.
caption: 'Employee Grid List',//Grid's Caption, you can set it to blank if you do not want this

Also there are some parameters which you should know, see above inline comments for their details.
altRows: true,//Use alternate row colors
altclass: 'jqgaltrow',//Alternate row class
hoverrows: true//Do not hover rows on mouse over
pager: $('#jq-grid-pager'),//Div in which pager will be displayed
toppager: true,
pgbuttons: true,//Show next/previous buttons
loadtext:'Loading Data please wait ...',
loadui :'block',//enable by default, block will disable inputs, remove this line if you do not want to block UI

I have created a function to load the data for grid whenever search or refresh button is pressed. Here is the code:-
    function reloadGrid(refresh) {
        if (refresh == true) {
            //Set initial values of your filters here
            $("#txtId").val("");
            $("#txtEmployeeName").val("");
    
            //reload grid's page 1 instead of current page
            $("#jq-grid").trigger("reloadGrid", { page: 1 });
        }
        else {
            $("#jq-grid").trigger("reloadGrid");
        }
    }
When I want to refresh grid, I set page:1, which will load the first page when ever you press refresh button.

Server Side Code
First Method which be called when Page is loaded is:-
[HttpGet]
public ActionResult Index()
{
    ViewBag.Filters = string.Empty;
    if (EmployeeGridRequest.DataFilters().Count > 0)
    {
        ViewBag.Filters = "$('#txtEmployeeName').val('" + EmployeeGridRequest.DataFilters()["EmployeeName"] + "');";
        ViewBag.Filters += "$('#txtId').val('" + EmployeeGridRequest.DataFilters()["Id"] + "');";
    }
 
    ViewBag.EmployeeGridRequest = EmployeeGridRequest;
    return View();
}
In above code I have checked from my session wrapper EmployeeGridRequest that if there is any filters available then use them to populate the initial values for the my filters and save in my ViewBag Object. As you can see below, in document.ready I have checked that if ViewBag contains filters then render them as Raw Html:-
$(document).ready(function () {
@if (ViewBag.Filters != string.Empty)
{
    @Html.Raw(ViewBag.Filters);
}

Now comes my main method which is responsible for providing data to grid. I have created some custom classes which encapsulate all the parameters which are used by jQGrid. So every time my implementation remains the same. See:-
[HttpGet]
        public JsonResult GetList(GridDataRequest request)
        {
            EmployeeGridRequest = request;
            var response = model.GetRecords(request);//Tricky Part Here :)
            var jsonData = new
            {
                total = response.TotalPages,
                page = request.page,
                records = response.TotalRecords,
                rows = response.Rows
            };
            return Json(jsonData, JsonRequestBehavior.AllowGet);
        }

Now in our model we have to create the list which we want to display in grid based on the parameters provided.
public GridDataResponse GetRecords(GridDataRequest request)
{
    .....
 GridDataResponse response = new GridDataResponse();
 response.TotalRecords = selectedList.Count;
 response.TotalPages = (int)Math.Ceiling((float)selectedList.Count() / (float)request.rows);
 response.Rows =
  (
  from item in selectedListAfterPagging
  select new GridDataDetail()
  {
   id = item.Id,
   cell = new string[] { 
   item.Id.ToString(),
   item.Name,
   "+item.Id.ToString()+"' title='Edit'>Edit " +
   "+item.Id.ToString()+"' title='Delete'>Delete"
   }
  }
  ).ToArray();
 return response;
}

I have used a List to store my Employee objects, this is only because I do not want to add a database to my sample, in real world off course you will be using database to access data. In above method I am creating a GridResponse object which will be bind with grid.
Since I am using custom paging instead of sending all records to client, so I have to manually calculate the no of records as i did:-
response.TotalPages = (int)Math.Ceiling((float)selectedList.Count() / (float)request.rows);

Also see that Grid detail object contain two properties i.e. id and cell. The id will be used as identified in grid and col will be bind to the columns in our grid.

Final Output

Take a look at our final output. First see how page is loaded first time:-
Now I have changed some sorting and applied filters to the grid.
Now edit some record by pressing edit link.
And press save button to get back to list page and see that our grid settings are persisted across page.

Finally its all done. And now you can see your grid in action. Hopefully this will be useful for you. Feel free to comment. Happy Coding !!!