5.7. Adapting the Controller to jqGrid
Now, we are going to change the CustomerController controller so that it works with jqGrid. The code is quite lengthy, so track the comments to get a sense of the way the controller works.
public class CustomerController : Controller{private DbModel db = new DbModel();// Display viewpublic ActionResult Index(){return View();}// Receiving data in JSON for gridpublic ActionResult GetData(int? rows, int? page, string sidx, string sord,string searchField, string searchString, string searchOper){// get the page number, the number of data displayedint pageNo = page ?? 1;int limit = rows ?? 20;// calculate the offsetint offset = (pageNo - 1) * limit;// building a query for suppliersvar customersQuery =from customer in db.CUSTOMERSselect new{CUSTOMER_ID = customer.CUSTOMER_ID,NAME = customer.NAME,ADDRESS = customer.ADDRESS,ZIPCODE = customer.ZIPCODE,PHONE = customer.PHONE};// adding a search condition to the query, if it is producedif (searchField != null){switch (searchOper){case "eq":customersQuery = customersQuery.Where(c => c.NAME == searchString);break;case "bw":customersQuery = customersQuery.Where(c => c.NAME.StartsWith(searchString));break;case "cn":customersQuery = customersQuery.Where(c => c.NAME.Contains(searchString));break;}}// get the total number of suppliersint totalRows = customersQuery.Count();// add sortingswitch (sord) {case "asc":customersQuery = customersQuery.OrderBy(customer => customer.NAME);break;case "desc":customersQuery = customersQuery.OrderByDescending(customer => customer.NAME);break;}// get the list of suppliersvar customers = customersQuery.Skip(offset).Take(limit).ToList();// calculate the total number of pagesint totalPages = totalRows / limit + 1;// create the result for jqGridvar result = new{page = pageNo,total = totalPages,records = totalRows,rows = customers};// convert the result to JSONreturn Json(result, JsonRequestBehavior.AllowGet);}// Adding a new supplier[HttpPost][ValidateAntiForgeryToken]public ActionResult Create([Bind(Include = "NAME,ADDRESS,ZIPCODE,PHONE")] CUSTOMER customer){// check the correctness of the modelif (ModelState.IsValid){// get a new identifier using a generatorcustomer.CUSTOMER_ID = db.NextValueFor("GEN_CUSTOMER_ID");// add the model to the listdb.CUSTOMERS.Add(customer);// save modeldb.SaveChanges();// return success in JSON formatreturn Json(true);}else {// join model errors in one stringstring messages = string.Join("; ", ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage));// return error in JSON formatreturn Json(new { error = messages });}}// Editing supplier[HttpPost][ValidateAntiForgeryToken]public ActionResult Edit([Bind(Include = "CUSTOMER_ID,NAME,ADDRESS,ZIPCODE,PHONE")] CUSTOMER customer){// check the correctness of the modelif (ModelState.IsValid){// mark the model as modifieddb.Entry(customer).State = EntityState.Modified;// save modeldb.SaveChanges();// return success in JSON formatreturn Json(true);}else {// join model errors in one stringstring messages = string.Join("; ", ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage));// return error in JSON formatreturn Json(new { error = messages });}}// Deleting supplier[HttpPost][ValidateAntiForgeryToken]public ActionResult Delete(int id){// find supplier by idCUSTOMER customer = db.CUSTOMERS.Find(id);// delete supplierdb.CUSTOMERS.Remove(customer);// save modeldb.SaveChanges();// return success in JSON formatreturn Json(true);}protected override void Dispose(bool disposing){if (disposing){db.Dispose();}base.Dispose(disposing);}}
The Index method is used to display the Views/Customer/Index.cshtml view. The view itself will be presented a bit later. This view is actually an html page template with markup and JavaScript for initiating jqGrid. The data itself will be obtained asynchronously in the JSON format, using the Ajax technology. The selected type of sorting, the page number and the search parameters will determine the format of an HTTP request that will be handled by the GetData action. The parameters of the HTTP request are displayed in the input parameters of the GetData method. We generate a LINQ query based on these parameters and send the retrieved result in the JSON format.
Various libraries can assist with parsing the parameters of a query generated by jqGrid and make it easier to build the model. We have not used them in our examples so the code might be somewhat cumbersome. You can always improve it, of course. |
The Create method is used to add a new customer record. The method has the [HttpPost] attribute specified for it to indicate that the parameters of the HTTP POST request () are to be displayed on the Customer model. Examine the following line:
[Bind(Include = "NAME,ADDRESS,ZIPCODE,PHONE")] CUSTOMER customer
Here Bind specifies which parameters of the HTTP request are to be displayed in the properties of the model.
5.7.1. The Attribute ValidateAntiforgeryToken
Note the ValidateAntiforgeryToken attribute. It is used to prevent forging requests between websites by verifying the tokens when the action method is called. The presence of this attribute requires that the HTTP request has an additional parameter named __RequestVerificationToken.
This parameter is automatically added to each form where the @Html.AntiForgeryToken() helper is specified. However, the jqGrid library uses dynamically generated Ajax requests rather than previously created web forms. To fix that, we need to change the shared view Views/Shared/_Layout.cshtml as follows:
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>@ViewBag.Title - ASP.NET application</title>@Styles.Render("~/Content/css")@Scripts.Render("~/bundles/modernizr")@Scripts.Render("~/bundles/jquery")@Scripts.Render("~/bundles/jquery-ui")<link href="~/Content/jquery.jqGrid/ui.jqgrid.css"rel="stylesheet" type="text/css" /><link href="~/Content/jquery.jqGrid/ui.jqgrid-bootstrap.css"rel="stylesheet" type="text/css" /><link href="~/Content/jquery.jqGrid/ui.jqgrid-bootstrap-ui.css"rel="stylesheet" type="text/css" /><script src="~/Scripts/jquery.jqGrid.min.js"type="text/javascript"></script><script src="~/Scripts/i18n/grid.locale-en.js"type="text/javascript"></script></head><body>@Html.AntiForgeryToken()<script>function GetAntiForgeryToken() {var tokenField =$("input[type='hidden'][name$='RequestVerificationToken']");if (tokenField.length == 0) {return null;} else {return {name: tokenField[0].name,value: tokenField[0].value};}}// add prefilter to all ajax requests// it will add to any POST ajax request// AntiForgery token$.ajaxPrefilter(function (options, localOptions, jqXHR) {if (options.type !== "GET") {var token = GetAntiForgeryToken();if (token !== null) {if (options.data.indexOf(""X-Requested-With") === -1) {options.data = "X-Requested-With=XMLHttpRequest"+ ((options.data === "") ? "" : "&" + options.data);}options.data = options.data + "&" + token.name + '='+ token.value;}}});// initialize the general properties of the jqGrid module$.jgrid.defaults.width = 780;$.jgrid.defaults.responsive = true;$.jgrid.defaults.styleUI = 'Bootstrap';</script><!-- Navigation menu --><div class="navbar navbar-inverse navbar-fixed-top"><div class="container"><div class="navbar-header"><button type="button" class="navbar-toggle" data-toggle="collapse"data-target=".navbar-collapse"><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"7gt;</span></button></div>><div class="navbar-collapse collapse"><ul class="nav navbar-nav"><li>@Html.ActionLink("Customers", "Index", "Customer")</li><li>@Html.ActionLink("Goods", "Index", "Product")</li><li>@Html.ActionLink("Invoices", "Index", "Invoice")</li></ul></div></div></div><div class="container body-content">@RenderBody()<hr /><footer><p>© @DateTime.Now.Year - ASP.NET application</p></footer></div>@Scripts.Render("~/bundles/bootstrap")@RenderSection("scripts", required: false)</body></html>
