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.

  1. public class CustomerController : Controller
  2. {
  3. private DbModel db = new DbModel();
  4. // Display view
  5. public ActionResult Index()
  6. {
  7. return View();
  8. }
  9. // Receiving data in JSON for grid
  10. public ActionResult GetData(int? rows, int? page, string sidx, string sord,
  11. string searchField, string searchString, string searchOper)
  12. {
  13. // get the page number, the number of data displayed
  14. int pageNo = page ?? 1;
  15. int limit = rows ?? 20;
  16. // calculate the offset
  17. int offset = (pageNo - 1) * limit;
  18. // building a query for suppliers
  19. var customersQuery =
  20. from customer in db.CUSTOMERS
  21. select new
  22. {
  23. CUSTOMER_ID = customer.CUSTOMER_ID,
  24. NAME = customer.NAME,
  25. ADDRESS = customer.ADDRESS,
  26. ZIPCODE = customer.ZIPCODE,
  27. PHONE = customer.PHONE
  28. };
  29. // adding a search condition to the query, if it is produced
  30. if (searchField != null)
  31. {
  32. switch (searchOper)
  33. {
  34. case "eq":
  35. customersQuery = customersQuery.Where(
  36. c => c.NAME == searchString);
  37. break;
  38. case "bw":
  39. customersQuery = customersQuery.Where(
  40. c => c.NAME.StartsWith(searchString));
  41. break;
  42. case "cn":
  43. customersQuery = customersQuery.Where(
  44. c => c.NAME.Contains(searchString));
  45. break;
  46. }
  47. }
  48. // get the total number of suppliers
  49. int totalRows = customersQuery.Count();
  50. // add sorting
  51. switch (sord) {
  52. case "asc":
  53. customersQuery = customersQuery.OrderBy(
  54. customer => customer.NAME);
  55. break;
  56. case "desc":
  57. customersQuery = customersQuery.OrderByDescending(
  58. customer => customer.NAME);
  59. break;
  60. }
  61. // get the list of suppliers
  62. var customers = customersQuery
  63. .Skip(offset)
  64. .Take(limit)
  65. .ToList();
  66. // calculate the total number of pages
  67. int totalPages = totalRows / limit + 1;
  68. // create the result for jqGrid
  69. var result = new
  70. {
  71. page = pageNo,
  72. total = totalPages,
  73. records = totalRows,
  74. rows = customers
  75. };
  76. // convert the result to JSON
  77. return Json(result, JsonRequestBehavior.AllowGet);
  78. }
  79. // Adding a new supplier
  80. [HttpPost]
  81. [ValidateAntiForgeryToken]
  82. public ActionResult Create(
  83. [Bind(Include = "NAME,ADDRESS,ZIPCODE,PHONE")] CUSTOMER customer)
  84. {
  85. // check the correctness of the model
  86. if (ModelState.IsValid)
  87. {
  88. // get a new identifier using a generator
  89. customer.CUSTOMER_ID = db.NextValueFor("GEN_CUSTOMER_ID");
  90. // add the model to the list
  91. db.CUSTOMERS.Add(customer);
  92. // save model
  93. db.SaveChanges();
  94. // return success in JSON format
  95. return Json(true);
  96. }
  97. else {
  98. // join model errors in one string
  99. string messages = string.Join("; ", ModelState.Values
  100. .SelectMany(x => x.Errors)
  101. .Select(x => x.ErrorMessage));
  102. // return error in JSON format
  103. return Json(new { error = messages });
  104. }
  105. }
  106. // Editing supplier
  107. [HttpPost]
  108. [ValidateAntiForgeryToken]
  109. public ActionResult Edit(
  110. [Bind(Include = "CUSTOMER_ID,NAME,ADDRESS,ZIPCODE,PHONE")] CUSTOMER customer)
  111. {
  112. // check the correctness of the model
  113. if (ModelState.IsValid)
  114. {
  115. // mark the model as modified
  116. db.Entry(customer).State = EntityState.Modified;
  117. // save model
  118. db.SaveChanges();
  119. // return success in JSON format
  120. return Json(true);
  121. }
  122. else {
  123. // join model errors in one string
  124. string messages = string.Join("; ", ModelState.Values
  125. .SelectMany(x => x.Errors)
  126. .Select(x => x.ErrorMessage));
  127. // return error in JSON format
  128. return Json(new { error = messages });
  129. }
  130. }
  131. // Deleting supplier
  132. [HttpPost]
  133. [ValidateAntiForgeryToken]
  134. public ActionResult Delete(int id)
  135. {
  136. // find supplier by id
  137. CUSTOMER customer = db.CUSTOMERS.Find(id);
  138. // delete supplier
  139. db.CUSTOMERS.Remove(customer);
  140. // save model
  141. db.SaveChanges();
  142. // return success in JSON format
  143. return Json(true);
  144. }
  145. protected override void Dispose(bool disposing)
  146. {
  147. if (disposing)
  148. {
  149. db.Dispose();
  150. }
  151. base.Dispose(disposing);
  152. }
  153. }

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:

  1. [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:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  5. <meta charset="utf-8" />
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title>@ViewBag.Title - ASP.NET application</title>
  8. @Styles.Render("~/Content/css")
  9. @Scripts.Render("~/bundles/modernizr")
  10. @Scripts.Render("~/bundles/jquery")
  11. @Scripts.Render("~/bundles/jquery-ui")
  12. <link href="~/Content/jquery.jqGrid/ui.jqgrid.css"
  13. rel="stylesheet" type="text/css" />
  14. <link href="~/Content/jquery.jqGrid/ui.jqgrid-bootstrap.css"
  15. rel="stylesheet" type="text/css" />
  16. <link href="~/Content/jquery.jqGrid/ui.jqgrid-bootstrap-ui.css"
  17. rel="stylesheet" type="text/css" />
  18. <script src="~/Scripts/jquery.jqGrid.min.js"
  19. type="text/javascript"></script>
  20. <script src="~/Scripts/i18n/grid.locale-en.js"
  21. type="text/javascript"></script>
  22. </head>
  23. <body>
  24. @Html.AntiForgeryToken()
  25. <script>
  26. function GetAntiForgeryToken() {
  27. var tokenField =
  28. $("input[type='hidden'][name$='RequestVerificationToken']");
  29. if (tokenField.length == 0) {
  30. return null;
  31. } else {
  32. return {
  33. name: tokenField[0].name,
  34. value: tokenField[0].value
  35. };
  36. }
  37. }
  38. // add prefilter to all ajax requests
  39. // it will add to any POST ajax request
  40. // AntiForgery token
  41. $.ajaxPrefilter(
  42. function (options, localOptions, jqXHR) {
  43. if (options.type !== "GET") {
  44. var token = GetAntiForgeryToken();
  45. if (token !== null) {
  46. if (options.data.indexOf(""X-Requested-With") === -1) {
  47. options.data = "X-Requested-With=XMLHttpRequest"
  48. + ((options.data === "") ? "" : "&" + options.data);
  49. }
  50. options.data = options.data + "&" + token.name + '='
  51. + token.value;
  52. }
  53. }
  54. }
  55. );
  56. // initialize the general properties of the jqGrid module
  57. $.jgrid.defaults.width = 780;
  58. $.jgrid.defaults.responsive = true;
  59. $.jgrid.defaults.styleUI = 'Bootstrap';
  60. </script>
  61. <!-- Navigation menu -->
  62. <div class="navbar navbar-inverse navbar-fixed-top">
  63. <div class="container">
  64. <div class="navbar-header">
  65. <button type="button" class="navbar-toggle" data-toggle="collapse"
  66. data-target=".navbar-collapse">
  67. <span class="icon-bar"></span>
  68. <span class="icon-bar"></span>
  69. <span class="icon-bar"7gt;</span>
  70. </button>
  71. </div>>
  72. <div class="navbar-collapse collapse">
  73. <ul class="nav navbar-nav">
  74. <li>@Html.ActionLink("Customers", "Index", "Customer")</li>
  75. <li>@Html.ActionLink("Goods", "Index", "Product")</li>
  76. <li>@Html.ActionLink("Invoices", "Index", "Invoice")</li>
  77. </ul>
  78. </div>
  79. </div>
  80. </div>
  81. <div class="container body-content">
  82. @RenderBody()
  83. <hr />
  84. <footer>
  85. <p>&copy; @DateTime.Now.Year - ASP.NET application</p>
  86. </footer>
  87. </div>
  88. @Scripts.Render("~/bundles/bootstrap")
  89. @RenderSection("scripts", required: false)
  90. </body>
  91. </html>