1
MVC – Ajax and Modal Views
AJAX stands for Asynchronous JavaScript and XML. It is a client-side capability that allows
silent updates of parts of a web page. Every browser provides a component called
XmlHttpRequest that is invoked via Javascript and has the capability to make asynchronous calls
back to the web server from where the page was obtained. One can set up Javascript timers to
automate the periodic retrieval of data using the XmlHttpRequest object.
Even though we can program the XmlHttpRequest object directly using Javascript, JQuery
makes it a lot easier to set it up and issue an asynchronous request to the web server. ASP.Net
MVC also provides a server-side solution to AJAX via the Ajax.BeginForm extension method.
We will demonstrate the two approaches to AJAX i.e., via JQuery and serializable partial views,
and via the Ajax.BeginForm.
Using SQL Server Management Studo, create a database called ProductsDB. Add a table called
Categories with the folling data.
The CategoryId column is of type int, and the CategoryName column is of type carchar(50).
Add another table called Products with the following design.
Then put the following data in the Products table.
2
Using Visual Studio, create a new project. Choose the web type of project, and select the MVC
template as shown below.
Add the following connection string information to the web.config file. Replace ALPHA with
the name of your database server.
<configuration>
<connectionStrings>
<add name="PRODUCTSDB" connectionString="server=ALPHA;integrated
security=true;database=ProductsDB" />
</connectionStrings>
<appSettings>
Add a class to the Models folder called Category with the following code in it.
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
}
3
Add a folder called DataLayer to the project. Then add an interface called IEntity to it with the
following code in it.
public interface IEntity
{ // any class that needs conversion to a List from a DataTable will implement this
void PopulateFields(DataRow dr);
}
Add a class called MyEntityBase to the Models folder with the following code in it. This class
uses reflection to populate the fields of a class from a given DataRow of a DataTable. All
repository methods that need to convert a DataTable to a list will indirectly use this method via
the generic DBList class..
public class MyEntityBase : IEntity
{
public void PopulateFields(DataRow dr)
{
// use reflection to populate the fields of this class from DataRow
Type tp = this.GetType();
foreach (PropertyInfo pi in tp.GetProperties())
pi.SetValue(this, dr[pi.Name]);
}
}
Add a class called DBList to the DataLayer folder with the following code in it.
class DBList
{
...
1 MVC – Ajax and Modal Views AJAX stands for Asynch.docx
1. 1
MVC – Ajax and Modal Views
AJAX stands for Asynchronous JavaScript and XML. It is a
client-side capability that allows
silent updates of parts of a web page. Every browser provides a
component called
XmlHttpRequest that is invoked via Javascript and has the
capability to make asynchronous calls
back to the web server from where the page was obtained. One
can set up Javascript timers to
automate the periodic retrieval of data using the
XmlHttpRequest object.
Even though we can program the XmlHttpRequest object
directly using Javascript, JQuery
makes it a lot easier to set it up and issue an asynchronous
request to the web server. ASP.Net
MVC also provides a server-side solution to AJAX via the
Ajax.BeginForm extension method.
We will demonstrate the two approaches to AJAX i.e., via
JQuery and serializable partial views,
2. and via the Ajax.BeginForm.
Using SQL Server Management Studo, create a database called
ProductsDB. Add a table called
Categories with the folling data.
The CategoryId column is of type int, and the CategoryName
column is of type carchar(50).
Add another table called Products with the following design.
Then put the following data in the Products table.
2
Using Visual Studio, create a new project. Choose the web type
of project, and select the MVC
template as shown below.
3. Add the following connection string information to the
web.config file. Replace ALPHA with
the name of your database server.
<configuration>
<connectionStrings>
<add name="PRODUCTSDB"
connectionString="server=ALPHA;integrated
security=true;database=ProductsDB" />
</connectionStrings>
<appSettings>
Add a class to the Models folder called Category with the
following code in it.
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
}
3
Add a folder called DataLayer to the project. Then add an
interface called IEntity to it with the
following code in it.
4. public interface IEntity
{ // any class that needs conversion to a List from a DataTable
will implement this
void PopulateFields(DataRow dr);
}
Add a class called MyEntityBase to the Models folder with the
following code in it. This class
uses reflection to populate the fields of a class from a given
DataRow of a DataTable. All
repository methods that need to convert a DataTable to a list
will indirectly use this method via
the generic DBList class..
public class MyEntityBase : IEntity
{
public void PopulateFields(DataRow dr)
{
// use reflection to populate the fields of this class from
DataRow
Type tp = this.GetType();
foreach (PropertyInfo pi in tp.GetProperties())
pi.SetValue(this, dr[pi.Name]);
}
}
Add a class called DBList to the DataLayer folder with the
following code in it.
5. class DBList
{
public static List<T> GetList<T>(DataTable dt)
where T : IEntity, new()
{ // will do conversion from dt to List<> for reference
types
List<T> TList = new List<T>();
foreach (DataRow dr in dt.Rows)
{
T t1 = new T();
// populate the columns from dr into the fields of the
t1
// delegate the work of populating t1 to the class T
t1.PopulateFields(dr);
TList.Add(t1);
}
return TList;
}
public static List<T> GetListValueType<T>(DataTable dt,
string colname)
where T : IConvertible // will do conversion from dt to
List<>for value
types including string
{
List<T> TList = new List<T>();
foreach (DataRow dr in dt.Rows)
TList.Add((T)dr[colname]);
return TList;
}
}
6. 4
Add a class called DBHelper to the DataLayer folder with the
following code in it. This class
helps in populating the parameters to a parameterized sql.
public class DBHelper
{
public static void AddSqlParam(List<SqlParameter> PList,
string paramName,
SqlDbType paramType, object paramValue, int size=0)
{
SqlParameter p = null;
if (size == 0)
p = new SqlParameter(paramName, paramType);
else
p = new SqlParameter(paramName, paramType,
size);
p.Value = paramValue;
PList.Add(p);
}
}
Add a class called Products to the Models folder with the
following code in it.
public class Product : MyEntityBase
{
//public int ProductId { get; set; }
int productId;
public int ProductId { get { return productId; } set {
7. productId = value; } }
public string ProductName { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public int StockLevel { get; set; }
public int CategoryId { get; set; }
public bool OnSale { get; set; }
public bool Discontinued { get; set; }
}
Since we will be display a dropdown with categories and a list
of products belonging to the
product category, we need to create a view model encapsulating
this information. Add a class
called ProductCatVM to the models folder with the following
code in it.
public class ProductCatVM
{
public List<SelectListItem> CatList { get; set; }
public List<Product> PList { get; set; }
public int categoryIdSelected { get; set; }
}
Add a class called RepositoryProducts to the Models folder with
the following code in it. It has
the methods to obtain products from the database, insert, update
and delete products.
8. public class RepositoryProducts
{ // purpose of Repository is to compose SQL, issue it to
DataAccess
// and return a strongly typed data to the caller
DataAccess _dataAccess = new DataAccess();
public List<Category> GetCategories()
5
{
List<Category> CList = new List<Category>();
try
{
string sql = "select * from Categories";
DataTable dt = _dataAccess.GetManyRowsCols(sql,
null);
// convert DataTable to List<Category>
foreach (DataRow dr in dt.Rows)
{
Category cat = new Category();
cat.CategoryId = (int) dr["CategoryId"];
cat.CategoryName = (string)dr["CategoryName"];
CList.Add(cat);
}
}
catch(Exception)
{
throw;
}
return CList;
}
public List<Product> GetProductsByCategory(int catid)
9. {
List<Product> PList = new List<Product>();
try
{
string sql = "select * from Products where
[email protected]";
List<SqlParameter> ParamList = new
List<SqlParameter>();
SqlParameter p1 = new SqlParameter("@categoryId",
SqlDbType.Int);
p1.Value = catid;
ParamList.Add(p1);
DataTable dt = _dataAccess.GetManyRowsCols(sql,
ParamList);
// convert DataTable to List<Product>
PList = DBList.GetList<Product>(dt);
}
catch (Exception)
{
throw;
}
return PList;
}
public bool AddProduct(Product pr)
{
bool ret = false;
try
{
string sql = "insert into Products
(ProductId,ProductName,
Description," +
"Price,StockLevel,CategoryId,OnSale,Discontinued) values
(@ProductId," +
14. List<SqlParameter> PList = new
List<SqlParameter>();
DBHelper.AddSqlParam(PList, "@ProductId",
SqlDbType.Int, pid);
int rows =
_dataAccess.InsertUpdateDelete(sql,ParamList);
if (rows > 0)
ret = true;
}
catch (Exception)
{
throw;
8
}
return ret;
}
}
By right clicking on the Controllers folder, add a controller
called ProductsController as shown
below.
Add the following actions to it.
public class ProductsController : Controller
15. {
Repository _rep = new Repository();
public ActionResult ShowProducts()
{
ProductCatVM pcvm = new ProductCatVM();
List<Category> CList = _rep.GetCategories();
// convert List<category> to List<SelectListItem> so
that it can
// be bound to a drop down
pcvm.CatList = new List<SelectListItem>();
foreach (Category cat in CList)
{
SelectListItem si = new SelectListItem
{
9
Value = cat.CategoryId.ToString(),
Text = cat.CategoryName
};
pcvm.CatList.Add(si);
}
pcvm.categoryIdSelected = CList[0].CategoryId;
pcvm.PList =
_rep.GetProductsByCategory(pcvm.categoryIdSelected);
return View(pcvm);
}
[HttpPost]
public ActionResult ShowProducts(ProductCatVM pcvm)
{
List<Category> CList = _rep.GetCategories();
16. // convert List<category> to List<SelectListItem> so
that it can
// be bound to a drop down
pcvm.CatList = new List<SelectListItem>();
foreach (Category cat in CList)
{
SelectListItem si = new SelectListItem
{
Value = cat.CategoryId.ToString(),
Text = cat.CategoryName
};
pcvm.CatList.Add(si);
}
pcvm.categoryIdSelected = pcvm.categoryIdSelected;
pcvm.PList =
_rep.GetProductsByCategory(pcvm.categoryIdSelected);
return View(pcvm);
}
}
Right click on the ShowProducts action, and choose “Add
View” with the following
specification.
10
Modify the code in the ShowProducts.cshtml to appear as:
18. {
<script>
$(function () {
// change event for the dropdown and post the form
back to server
$("#catgeoryIdSelected").css({ 'background-color':
'#4499ee', 'color':
'yellow' })
$("#categoryIdSelected").change(function () {
$(this).closest('form').submit();
})
})
</script>
}
By right clicking on the Products folder (under the Views
folder, add a partial view called
_ShowProducts as shown beow.
11
The generated code for the partial view _ShowProducts.cshtml
appears as:
@model IEnumerable<ProductsApp.Models.Product>
20. 12
<td>
@Html.DisplayFor(modelItem => item.Description)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.DisplayFor(modelItem => item.StockLevel)
</td>
<td>
@Html.DisplayFor(modelItem => item.OnSale)
</td>
<td>
@Html.DisplayFor(modelItem => item.Discontinued)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new {
id=item.ProductId }) |
@Html.ActionLink("Details", "Details", new {
id=item.ProductId }) |
@Html.ActionLink("Delete", "Delete", new {
id=item.ProductId })
</td>
</tr>
}
</table>
Add a link to the “ShowProducts” action in the _Layout.cshtml.
21. <ul class="nav navbar-nav">
<li>@Html.ActionLink("Home", "Index",
"Home")</li>
<li>@Html.ActionLink("About", "About",
"Home")</li>
<li>@Html.ActionLink("Contact", "Contact",
"Home")</li>
<li>@Html.ActionLink("Show Products",
"ShowProducts",
"Products")</li>
</ul>
Build and test the application.
13
AJAX using plain Javascript and JQuery:
As explained earlier, every browser provides the
XmlHttpRequest component that has the
capability to make asynchronous calls back to the server where
the page was obtained from,
obtain the data and update part of the page in a silent manner.
Add the following actions to the home controller.
22. public ActionResult GetStockPrice()
{
ViewBag.Symbol = "IBM";
return View("GetStockPrice");
}
public ActionResult GetStockPriceAjax(string symbol)
{
string url =
"http://www.nasdaq.com/aspx/infoquotes.aspx?symbol=" +
symbol;
WebClient wc = new WebClient();
string response = wc.DownloadString(url);
// parse the response for
int pos1 = response.IndexOf("LastSale1'>");
int pos2 = response.IndexOf("$ ", pos1 + 1);
int pos3 = response.IndexOf("</label>", pos2 + 1);
string price = response.Substring(pos2 + 7, pos3 - pos2
- 7).Trim();
return Content(price);
}
The first action will send a simple form to the user so that the
user can type in a stock symbol
and using the Javascript invoke the second action i.e.,
“GetStockPriceAjax” via
XmlHttpRequest. The action “GetStockPriceAjax” further
makes a socket connection to the
Nasdaq web site using the WebClient class, obtains the
response, and then filters it for just the
stock price, and then returns it as a simple string back to the
23. caller.
Add a view by right clicking on the GetStockPrice action and
choosing “Add View”.
Modify the code in the GetStockPrice.cshtml to appear as:
14
@{
ViewBag.Title = "GetStockPrice";
}
<h2>Stock Price</h2>
@using (Html.BeginForm())
{
string symbol = ViewBag.Symbol;
@Html.Label("Enter Stock Symbol")
@Html.EditorFor(x=>symbol)
<input type="button" onclick="getStock()"
name="btnGetPrice" value="Get Stock
Price Via Ajax" />
}
Stock Price : <span id="stkPrice"></span>
<hr/>
<div id="status"></div>
@section Scripts {
<script>
24. var xmlhttp; // global variable
var count = 0; // global variable
function getStock() {
count++; // count = count + 1
document.getElementById("status").innerHTML = "count
= " +count;
getStockPrice();
setTimeout("getStock()", 8000); // 8 sec timer
}
function getStockPrice() {
xmlhttp = new XMLHttpRequest(); // ajax component
if (xmlhttp) // see if it got created properly
{
xmlhttp.onreadystatechange = myCallback;
xmlhttp.open("GET", "GetStockPriceAjax?symbol=" +
document.getElementById("symbol").value, true);
xmlhttp.send(); // makes the socket connection
}
function myCallback() {
if (xmlhttp.readyState == "4") // response is loaded
{
if (xmlhttp.status == "200") // OK
{
price = xmlhttp.responseText;
document.getElementById("stkPrice").innerHTML
= price
}
}
}
}
</script>
}
25. The above code uses a Javascript timer to periodically call the
getStock function every 8
seconds. This function further calls the getStockPrice function
which uses Ajax to trigger the
15
server side action GetStockPriceAjax and passes it the stock
symbol using the get request. Once
the callback function receives the stock price, it displays it in a
span.
Add a link to the GetStockPrice action in the _Layout.cshtml.
<li>@Html.ActionLink("Get Stock Price",
"GetStockPrice", "Home")</li>
Build and test the application. You will notice that the stock
price will quietly get updated every
eight seconds (if the stock market is active i.e., between 9 a.m.
and 4 p.m.)
You will have to click on the “Get Stock Price Via Ajax” button
one time. If you change the
26. symbol to any other e.g., AAPL, the price will change for the
symbol, when the 8 second timer
expires and the Javascript makes another call to the server.
Instead of using XmlHttpRequest object directly, we can use
JQuery to make ajax calls back to
the server. JQuery provides $.getJSON and $.ajax methods for
this purpose.
Add an action called GetStockPriceJQ to the Home controller as
shown below.
public ActionResult GetStockPriceJQ()
{
ViewBag.Symbol = "IBM";
return View();
}
Add the corresponding view by right clicking on the above
action and choosing “Add View”.
Choose empty template. Type the following code in it.
@{
ViewBag.Title = "GetStockPriceJQ";
}
<h2>Get Stock Price JQ</h2>
27. @using (Html.BeginForm())
{
string symbol = ViewBag.Symbol;
16
@Html.Label("Enter Stock Symbol")
@Html.EditorFor(x => symbol)
<input type="button" onclick="getStock()"
name="btnGetPrice" value="Get Stock
Price Via Ajax" />
}
Stock Price : <span id="stkPrice"></span>
<hr />
<div id="status"></div>
@section Scripts {
<script>
var xmlhttp; // global variable
var count = 0; // global variable
function getStock() {
count++; // count = count + 1
$("#status").html("count = " +count);
getStockPrice();
setTimeout("getStock()", 8000); // 8 sec timer
}
function getStockPrice() {
var url = "GetStockPriceAjax?symbol=" +
$("#symbol").val();
$.getJSON(url, function (result) {
$("#stkPrice").html(result);
});
28. }
</script>
}
The above code uses getJSON method of JQuery to make an
ajax call to the web server. Note
that it internally uses the XmlHttpRequest component to make
the asynchronous call.
Add a link to the GetStockPriceJQ in the _Layout.cshtml file.
<li>@Html.ActionLink("Stock Price JQ",
"GetStockPriceJQ", "Home")</li>
Build and test the above view. It should behave same as the
GetStockPrice.
17
AJAX in MVC:
For more involved views, where a portion of the view needs to
be silently updated by going back
29. to the server, there are two approaches that we can use in MVC.
The first to use JQuery on the
client side which will make an ajax call to the server. The
server should return a partial view in
this case. The partial view has to be serialized to a JSON string
in this technique.
The second technique requires minimal Javascript and the ajax
behavior is accomplished via the
Ajax.BeginForm() extension method. It emits the proper
Javacsript to the browser.
Since in the JQuery approach, the view has to be serialized as a
JSON string, add a class called
ControllerBase to the Controllers folder with the following code
in it.
public class ControllerBase : Controller
{
public string SerializeControl(string controlPath, object
model)
{
ViewResult v = View();
if (String.IsNullOrEmpty(v.ViewName))
v.ViewName =
RouteData.GetRequiredString("action");
ViewEngineResult result = null;
StringBuilder sb = new StringBuilder();
StringWriter textWriter = new StringWriter(sb);
HtmlTextWriter htmlWriter = new
30. HtmlTextWriter(textWriter);
if (v.View == null)
{
result = new ViewEngineResult(new
RazorView(this.ControllerContext,
controlPath, "", false, null), new
RazorViewEngine());
v.View = result.View;
}
ViewContext viewContext = new
ViewContext(ControllerContext, v.View,
ViewData, TempData, htmlWriter);
viewContext.ViewData.Model = model;
v.View.Render(viewContext, htmlWriter);
string rval = sb.ToString();
htmlWriter.Close();
textWriter.Close();
return rval;
}
public JsonResult ReturnJsonGet(string code, string result,
string errors)
{
return Json(new { code = code, data = result, errors =
errors },
JsonRequestBehavior.AllowGet);
}
}
The above code provides capability to be able to serialize any
view (i.e., partial view in practical
cases) as a JSON string.
31. Modify the ProductsController to inherit from the
ControllerBase as:
public class ProductsController : ControllerBase
18
Add the following actions to the ProductsController.
public ActionResult ShowProductsAjax()
{
ProductCatVM pcvm = new ProductCatVM();
List<Category> CList = _rep.GetCategories();
// convert List<category> to List<SelectListItem> so
that it can
// be bound to a drop down
pcvm.CatList = new List<SelectListItem>();
foreach (Category cat in CList)
{
SelectListItem si = new SelectListItem
{
Value = cat.CategoryId.ToString(),
Text = cat.CategoryName
};
pcvm.CatList.Add(si);
}
pcvm.categoryIdSelected = CList[0].CategoryId;
pcvm.PList =
_rep.GetProductsByCategory(pcvm.categoryIdSelected);
return View(pcvm);
}
32. public ActionResult GetProductsPartial(int id) // id is the
categoryid
{
List<Product> PList = _rep.GetProductsByCategory(id);
if (Request.IsAjaxRequest())
{
return ReturnJsonGet("200",
SerializeControl("~/Views/Products/_ShowProducts.cshtml",
PList), "");
}
return PartialView("_ShowProducts", PList);
}
The GetProductsPartial method shown above will be invoked by
JQuery from the client, and as
you can see, it returns the serialized partial view which the
JQuery will render in the browser.
Add the view for ShowProductsAjax.
19
Type the following code in ShowProductsAjax.cshtml.
@model ProductsApp.Models.ProductCatVM
33. @{
ViewBag.Title = "ShowProducts";
}
<h2>ShowProducts</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>ProductCatVM</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-
danger" })
<div class="row">
<div class="col-md-2">
@Html.Label("Category", htmlAttributes: new {
@class = "control-
label" })
@Html.DropDownListFor(model =>
model.categoryIdSelected,
Model.CatList, new { htmlAttributes = new { @class = "form-
control" } })
</div>
<div class="col-md-10" id="divProducts">
<!-- product for a selected category will be displayed
here via
JQuery-->
</div>
</div>
</div>
}
34. <div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts
{
<script>
$(function () {
$("#catgeoryIdSelected").css({ 'background-color':
'#4499ee', 'color':
'yellow' })
$.getJSON("/Products/GetProductsPartial/" +
$("#categoryIdSelected").val(),
function (data) {
if (data)
$("#divProducts").html(data.data);
else
$("#divProducts").html("problem in loading
data");
});
20
$("#categoryIdSelected").change(function () {
//$(this).closest('form').submit();
// make an ajax call to receive the partialview for
products, then
// render it in divProducts
$("#divProducts").html("Please wait, loading
data..");
35. $.getJSON("/Products/GetProductsPartial/" +
$("#categoryIdSelected").val(),
function (data) {
if (data)
$("#divProducts").html(data.data);
else
$("#divProducts").html("problem in loading
data");
});
});
});
</script>
}
The above code makes an ajax call using getJSON to the
GetProductsPartial action that returns
the JSON serialized partial view.
Add a link for GetProductsAjax to the _Layout.cshtml.
<li>@Html.ActionLink("Show Products Ajax",
"ShowProductsAjax", "Products")</li>
Build and test the above capability.
If you make a selection change in the category dropdown, you
will notice that the corresponding
products are obtained from the server and displayed quietly
without the entire browser doing a
36. refresh.
Since the trip back to the web server may be slow depending
upon the internet availability, it is a
good practice to let the user know that the data is being loaded
whenever ajax is involved. To
simulate this behavior, add a Thread.Sleep statement to the
GetProductsPartial action as shown
below.
21
public ActionResult GetProductsPartial(int id) // id is the
categoryid
{
Thread.Sleep(4000);
List<Product> PList = _rep.GetProductsByCategory(id);
if (Request.IsAjaxRequest())
{
return ReturnJsonGet("200",
SerializeControl("~/Views/Products/_ShowProducts.cshtml",
PList), "");
}
return PartialView("_ShowProducts", PList);
37. }
Now test the GetProductsAjax page. You will see the “Please
wait, loading data” message for a
few seconds before displaying the products information.
Ajax Using Ajax.BeginForm():
An alterative to the above ajax approach is to use the
Ajax.BeginForm. This requires
minimal javascript and also does not require serialization of a
partial view. The same
GetProductsAjax behavior can be accomplished by the
following actions and the corresponding
view.
public ActionResult ProductsViaAF()
{
ProductCatVM pcvm = new ProductCatVM();
List<Category> CList = _rep.GetCategories();
// convert List<category> to List<SelectListItem> so
that it can
// be bound to a drop down
pcvm.CatList = new List<SelectListItem>();
foreach (Category cat in CList)
{
SelectListItem si = new SelectListItem
{
38. Value = cat.CategoryId.ToString(),
Text = cat.CategoryName
};
pcvm.CatList.Add(si);
}
22
pcvm.categoryIdSelected = CList[0].CategoryId;
pcvm.PList =
_rep.GetProductsByCategory(pcvm.categoryIdSelected);
return View(pcvm);
}
public ActionResult GetProductsAF(string
CategoryIdSelected)
{
List<Product> PList = null;
if (CategoryIdSelected != null)
PList =
_rep.GetProductsByCategory(int.Parse(CategoryIdSelected));
else
PList =
_rep.GetProductsByCategory(int.Parse("100"));
return PartialView("_ShowProducts", PList);
}
Add a view called ProductsViaAF.
Type the following code in it.
40. be displayed here
})) // no need to serialize the partial
view
{
@Html.Label("Category", htmlAttributes: new {
@class = "control-
label" })
@Html.DropDownListFor(model =>
model.categoryIdSelected,
Model.CatList, new { htmlAttributes = new { @class = "form-
control" } })
}
</div>
<div class="col-md-10" style="display:none"
id="divLoading">
Please wait, loading data..</div>
<div class="col-md-10" id="divProducts">
@{
Html.RenderPartial("_ShowProducts", Model.PList);
}
</div>
</div>
</div>
<div id="#divStatus"></div>
@section Scripts
{
$(function () {
$("#catgeoryIdSelected").css({ 'background-color':
'#4499ee', 'color':
'yellow' })
$("#categoryIdSelected").change(function () {
$("#divLoading").html("Please wait, loading data..");
$("#divProducts").html("");
$(this).closest('form').submit();
});
41. })
function ProductsLoadFailed(response) {
$("#divLoading").html("problem in receiving data..")
}
function ProductsLoadOK(response) {
$("#divStatus").html("data loaded..")
}
</script>
}
The above code uses the Ajax.BeginForm to set up an ajax
request back to the web server.
Through the UpdateTargetId, the display for the partial view
brought back is specified. The first
parameter to the Ajax.BeginForm method indicates the action
that will be triggered via the ajax
call.
24
Add a link to the ProductsViaAF action in the _Layout.cshtml
page.
<li>@Html.ActionLink("Show Products AF",
"ProductsViaAF", "Products")</li>
42. If you build and trigger the “Show Products AF” link and then
change the category, you will
notice that the category drop down disappears and the browser
also does a full reload instead of a
silent update for the products corresponding to the category
selected. This is because to be able
to use the Ajax.BeginForm, you need the Microsoft Unobtrusive
Ajax library.
Using the Project->Manage Nuget Packages, browse for
Unobtrusive library as shown below,
and then click install.
25
After the unobtrusive library is installed, add a reference to it in
the ProductsViaAF.cshtml file
as shown below (in bold).
@section Scripts
43. {
<script src="~/Scripts/jquery.unobtrusive-
ajax.min.js"></script>
<script>
$(function () {
$("#catgeoryIdSelected").css({ 'background-color':
'#4499ee', 'color':
'yellow' })
Now if you try the ProductsViaAF, it will have the proper Ajax
behavior.
Modal Views in MVC:
Modal views can be added to an application by using the JQuery
showDialog() method. The
modal view itself is a partial view i.e., the javascript triggers an
action that returns a partial view.
We will provide capability to be able to create a new product,
be able to edit an existing product
and be able to delete a product. For these purposes, add a class
called ProductCreateEditVM to
the models folder with the following code in it. This will be
used in both creating a new product
and editing an existing product.
public class ProductCreateEditVM
44. {
public List<SelectListItem> CatList { get; set; }
public Product Prod { get; set; }
public int CategoryIdSelected { get; set; }
}
For modal dialogs using JQuery, we need to download the
jquery ui library using the Project-
>Manage Nuget packages, as shown below.
Search for “jquery ui” and install it in your project.
26
In the App_Start folder, add the following statements (shown in
bold) to the Bundle.Config.cs
file.
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new
ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
46. 27
ProductCatVM pcvm = new ProductCatVM();
List<Category> CList = _rep.GetCategories();
pcvm.CatList = new List<SelectListItem>();
foreach (Category cat in CList)
{
SelectListItem si = new SelectListItem
{
Value = cat.CategoryId.ToString(),
Text = cat.CategoryName
};
pcvm.CatList.Add(si);
}
pcvm.categoryIdSelected = CList[0].CategoryId;
pcvm.PList =
_rep.GetProductsByCategory(pcvm.categoryIdSelected);
return View(pcvm);
}
[HttpPost]
public ActionResult ProductsModal(ProductCatVM pcvm)
{ // once the ProductCatVM is posted to us, we need to
repopulate the
dropdown
List<Category> CList = _rep.GetCategories();
// convert List<category> to List<SelectListItem> so
that it can
// be bound to a drop down
pcvm.CatList = new List<SelectListItem>();
foreach (Category cat in CList)
{
SelectListItem si = new SelectListItem
{
47. Value = cat.CategoryId.ToString(),
Text = cat.CategoryName
};
pcvm.CatList.Add(si);
}
pcvm.PList =
_rep.GetProductsByCategory(pcvm.categoryIdSelected);
return View(pcvm);
}
public ActionResult CreateProduct()
{
ProductCreateEditVM pcevm = new
ProductCreateEditVM();
List<Category> CList = _rep.GetCategories();
// convert List<category> to List<SelectListItem> so
that it can
// be bound to a drop down
pcevm.CatList = new List<SelectListItem>();
foreach (Category cat in CList)
{
SelectListItem si = new SelectListItem
{
Value = cat.CategoryId.ToString(),
Text = cat.CategoryName
};
pcevm.CatList.Add(si);
}
pcevm.CategoryIdSelected = CList[0].CategoryId;
28
48. pcevm.Prod = new Product();
return View(pcevm);
}
[HttpPost]
public ActionResult CreateProduct(ProductCreateEditVM
pcevm)
{
// always check if model state is valid before
inserting/updating
database
if (!ModelState.IsValid)
{
return View(pcevm);
}
List<Category> CList = _rep.GetCategories();
// convert List<category> to List<SelectListItem> so
that it can
// be bound to a drop down
pcevm.Prod.CategoryId = pcevm.CategoryIdSelected;
pcevm.CatList = new List<SelectListItem>();
bool ret = false;
try
{
ret = _rep.AddProduct(pcevm.Prod);
}
catch(Exception ex)
{
ViewBag.Status = ex.Message;
}
foreach (Category cat in CList)
{
SelectListItem si = new SelectListItem
{
Value = cat.CategoryId.ToString(),
49. Text = cat.CategoryName
};
pcevm.CatList.Add(si);
}
if (ret)
ViewBag.Status = "Product created successfully..";
return View(pcevm);
}
public ActionResult EditProduct(int id) // has to be called
id
{
ProductCreateEditVM pcevm = new
ProductCreateEditVM();
pcevm.Prod = _rep.GetProduct(id);
List<Category> CList = _rep.GetCategories();
// convert List<category> to List<SelectListItem> so
that it can
// be bound to a drop down
pcevm.CatList = new List<SelectListItem>();
foreach (Category cat in CList)
{
SelectListItem si = new SelectListItem
{
Value = cat.CategoryId.ToString(),
29
Text = cat.CategoryName
};
pcevm.CatList.Add(si);
}
pcevm.CategoryIdSelected = pcevm.Prod.CategoryId;
50. // return View(pcevm); //for non modal
return PartialView("_EditProduct", pcevm);
}
[HttpPost]
public ActionResult EditProduct(ProductCreateEditVM
pcevm)
{
// always check if model state is valid before
inserting/updating
database
if (!ModelState.IsValid)
{
return View(pcevm);
}
List<Category> CList = _rep.GetCategories();
// convert List<category> to List<SelectListItem> so
that it can
// be bound to a drop down
pcevm.Prod.CategoryId = pcevm.CategoryIdSelected;
pcevm.CatList = new List<SelectListItem>();
bool ret = false;
try
{
ret = _rep.UpdateProduct(pcevm.Prod);
}
catch (Exception ex)
{
ViewBag.Status = ex.Message;
}
foreach (Category cat in CList)
{
SelectListItem si = new SelectListItem
{
Value = cat.CategoryId.ToString(),
Text = cat.CategoryName
51. };
pcevm.CatList.Add(si);
}
if (ret)
ViewBag.Status = "Product updated successfully..";
// return View(pcevm);
return Json(new { result = true, responseText = "update
successfull.."
});
}
public ActionResult DeleteProduct(string id, string did)
{ // id is catid, did is the product id
try
{
bool ret = _rep.DeleteProduct(int.Parse(did));
if (ret)
return new HttpStatusCodeResult(200);
30
else
return new HttpStatusCodeResult(500);
}
catch (Exception ex)
{
TempData["Message"] = ex.Message;
return new HttpStatusCodeResult(500);
}
}
Add a view called ProductsModal to the Products folder (under
52. the views folder) with an empty
template.
Modify the code in the ProductsModal.cshtml to appear as:
@model ProductsApp.Models.ProductCatVM
@{
ViewBag.Title = "ProductsModal";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create New", "CreateProduct")
</p>
@using (Html.BeginForm())
{
<table class="table">
<tr>
<td>
@Html.Label("Category") <br />
@Html.DropDownListFor(x => x.categoryIdSelected,
Model.CatList)
</td>
<td>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model =>
model.PList[0].ProductId)
</th>
<th>
@Html.DisplayNameFor(model =>
model.PList[0].ProductName)
54. </td>
<td>
@Html.DisplayFor(modelItem =>
item.ProductName)
</td>
<td>
@Html.DisplayFor(modelItem =>
item.Description)
</td>
<td>
@Html.DisplayFor(modelItem =>
item.Price)
</td>
<td>
@Html.DisplayFor(modelItem =>
item.StockLevel)
</td>
<td>
@Html.DisplayFor(modelItem =>
item.OnSale)
</td>
<td>
@Html.DisplayFor(modelItem =>
item.Discontinued)
</td>
<td>
@Html.ActionLink("Edit", "EditProduct",
new { id =
item.ProductId }, new { @class = "myedit" }) |
@Html.ActionLink("Details", "Details",
new { id =
item.ProductId }) |
@Html.ActionLink("Delete", "Delete",
new { id =
item.ProductId }, new { @class = "mydelete" })
</td>
55. </tr>
}
</table>
</td>
</tr>
</table>
}
<div>@ViewBag.Message</div>
<div id="EditDialogDiv" style="display:none"></div>
<div id="dlgConfirm" style="display:none" title="Delete
Selected Row?">
<p>
<span class="ui-icon ui-icon-alert"
style="float:left;margin:0 7px 20px
0"></span>
Selected Row Delete, Confirm?
</p>
</div>
@section Scripts
{
<script src="~/Scripts/jquery-ui-1.12.1.min.js"></script>
<script>
$(function () {
32
// write the change event for the dropdown and post the
form back to server
$("#catgeoryIdSelected").css({ 'background-color':
'#4499ee', 'color':
'yellow' })
56. $("#categoryIdSelected").change(function () {
//alert('ok')
$(this).closest('form').submit();
});
//-------------delete handling--------------------------
$(".mydelete").on("click", function (e) {
// determine the row where delete is clicked
var rowDel = $(this).closest('tr');
$("#dlgConfirm").show(); // make the div dlgConfirm
visible
$("#dlgConfirm").dialog({
buttons: {
"Confirm": function () {
$(this).dialog("close");
// determine the productid for the row
pid = rowDel.children('td:first').text();
$.ajax({
url: "DeleteProduct/" +
@Model.categoryIdSelected +
"?did=" + pid,
success: function () {
rowDel.remove().draw();
$("#dlgConfirm").hide(); // make the
div invisible
},
error: function (xmlhttp, textStatus,
errorThrown) {
alert(errorThrown);
}
});
},
"Cancel": function () {
$(this).dialog("close");
}
},
58. function
},
Cancel: function () {
$(this).dialog("close");
}
}
});
}
this.closeDialog = function () {
$(this).dialog("close");
}
}
$(".myedit").on("click", function (e) { // click event
for the edit
hyperlink
dialogEdit = new DialogEdit();
url = $(this).attr('href');
dialogEdit.showDialog(url);
e.preventDefault(); // cancel the click event
return false;
});
});
function editProductProcessing(posturl) {
// use ajax to post data back to server
$.ajax({
url: posturl,
type: "POST",
data: $('form').serialize(),
success: function (data) {
if (data) {
if (data.result == true) {
$("#divStatus").html(data.responseText).removeClass("text-
59. danger");
$("#divStatus").html(data.responseText).addClass("text-
success");
}
}
},
error: function (XMLHttpRequest, textStatus,
errorThrown) {
$("#divStatus").removeClass("text-success");
$("#divStatus").html(textStatus + " Error:" +
errorThrown).addClass("text-danger");
}
});
}
</script>
}
Add a link to the ProductsModal in the _Layout.cshtml.
<li>@Html.ActionLink("Products Modal",
"ProductsModal", "Products")</li>
34
Study the code in the controller actions for creating, editing and
deleting a product. Note that the
editing and deleting of products is initiated from the
ProductsModal view via links in each
product row being displayed by the following code.
60. @Html.ActionLink("Edit", "EditProduct", new { id =
item.ProductId }, new { @class =
"myedit" }) |
@Html.ActionLink("Details", "Details", new { id =
item.ProductId }) |
@Html.ActionLink("Delete", "Delete", new { id =
item.ProductId }, new { @class =
"mydelete" })
A css class of “myedit” is assigned to the edit link, and a css
class of “mydelete” is assigned to
the delete link. The javascript (JQuery) code in the
ProductsModal.cshtml uses these css classes
to issue an ajax call back to the server to obtain a partial view
for editing an existing product, or
showing a delete confirmation when the user clicks on the
delete link.
For example, when the user clicks on the delete link for a row,
the following javascript and the
html in the ProductsModal.cshtml displays the modal delete
confirmation dialog.
<div id="dlgConfirm" style="display:none" title="Delete
Selected Row?">
<p>
61. <span class="ui-icon ui-icon-alert"
style="float:left;margin:0 7px 20px
0"></span>
Selected Row Delete, Confirm?
</p>
</div>
//-------------delete handling--------------------------
$(".mydelete").on("click", function (e) {
// determine the row where delete is clicked
var rowDel = $(this).closest('tr');
$("#dlgConfirm").show(); // make the div dlgConfirm
visible
$("#dlgConfirm").dialog({
buttons: {
"Confirm": function () {
$(this).dialog("close");
// determine the productid for the row
pid = rowDel.children('td:first').text();
$.ajax({
url: "DeleteProduct/" +
@Model.categoryIdSelected +
"?did=" + pid,
35
success: function () {
rowDel.remove().draw();
$("#dlgConfirm").hide(); // make the
div invisible
},
error: function (xmlhttp, textStatus,
errorThrown) {
62. alert(errorThrown);
}
});
},
"Cancel": function () {
$(this).dialog("close");
}
},
"modal":true
});
e.preventDefault();
return false;
});
If the user clicks on the confirm button, the above javascript
makes an ajax call to the
DeleteProduct action and passes it the category id and the
product id.
Similarly, study the code in the ProductsModal.cshtml for
editing a product.
There is a link to the create product in the beginning of the
ProductsModal.cshtml. Add a view
for the CreateProduct by choosing a template of create and
model class of
ProductCreateEditVM. Modify the code in CreateProduct.cshtml
to appear as:
67. class="btn btn-default"
/>
</div>
</div>
</div>
<div>@ViewBag.Status</div>
}
<div>
@Html.ActionLink("Back to List", "ProductsModal")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
38
For editing a product, we need a partial view, as the editing
view will be displayed in a modal
dialog. Add a partial view to the Products folder (under the
Views) with the following
specification.
Modify the code in the _EdtProduct.cshtml to appear as:
@model ProductsApp.Models.ProductCreateEditVM