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 view
public ActionResult Index()
{
return View();
}
// Receiving data in JSON for grid
public ActionResult GetData(int? rows, int? page, string sidx, string sord,
string searchField, string searchString, string searchOper)
{
// get the page number, the number of data displayed
int pageNo = page ?? 1;
int limit = rows ?? 20;
// calculate the offset
int offset = (pageNo - 1) * limit;
// building a query for suppliers
var customersQuery =
from customer in db.CUSTOMERS
select 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 produced
if (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 suppliers
int totalRows = customersQuery.Count();
// add sorting
switch (sord) {
case "asc":
customersQuery = customersQuery.OrderBy(
customer => customer.NAME);
break;
case "desc":
customersQuery = customersQuery.OrderByDescending(
customer => customer.NAME);
break;
}
// get the list of suppliers
var customers = customersQuery
.Skip(offset)
.Take(limit)
.ToList();
// calculate the total number of pages
int totalPages = totalRows / limit + 1;
// create the result for jqGrid
var result = new
{
page = pageNo,
total = totalPages,
records = totalRows,
rows = customers
};
// convert the result to JSON
return 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 model
if (ModelState.IsValid)
{
// get a new identifier using a generator
customer.CUSTOMER_ID = db.NextValueFor("GEN_CUSTOMER_ID");
// add the model to the list
db.CUSTOMERS.Add(customer);
// save model
db.SaveChanges();
// return success in JSON format
return Json(true);
}
else {
// join model errors in one string
string messages = string.Join("; ", ModelState.Values
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage));
// return error in JSON format
return 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 model
if (ModelState.IsValid)
{
// mark the model as modified
db.Entry(customer).State = EntityState.Modified;
// save model
db.SaveChanges();
// return success in JSON format
return Json(true);
}
else {
// join model errors in one string
string messages = string.Join("; ", ModelState.Values
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage));
// return error in JSON format
return Json(new { error = messages });
}
}
// Deleting supplier
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(int id)
{
// find supplier by id
CUSTOMER customer = db.CUSTOMERS.Find(id);
// delete supplier
db.CUSTOMERS.Remove(customer);
// save model
db.SaveChanges();
// return success in JSON format
return 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>