SlideShare a Scribd company logo
1 of 72
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
{
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;
}
}
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 {
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.
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)
{
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," +
"@ProductName,@Description,@Price,@StockLevel,@Categor
yId,@OnSale," +
"@Discontinued)";
6
List<SqlParameter> PList = new
List<SqlParameter>();
DBHelper.AddSqlParam(PList, "@ProductId",
SqlDbType.Int,
pr.ProductId);
DBHelper.AddSqlParam(PList,"@ProductName",
SqlDbType.VarChar,
pr.ProductName,50);
DBHelper.AddSqlParam(PList, "@Description",
SqlDbType.Text,
pr.Description);
DBHelper.AddSqlParam(PList, "@Price",
SqlDbType.Money, pr.Price);
DBHelper.AddSqlParam(PList, "@StockLevel",
SqlDbType.Int,
pr.StockLevel);
DBHelper.AddSqlParam(PList, "@CategoryId",
SqlDbType.Int,
pr.CategoryId);
DBHelper.AddSqlParam(PList, "@OnSale",
SqlDbType.Bit, pr.OnSale);
DBHelper.AddSqlParam(PList, "@Discontinued",
SqlDbType.Bit,
pr.Discontinued);
int rowsModified =
_dataAccess.InsertUpdateDelete(sql, PList);
if (rowsModified > 0)
ret = true;
}
catch (Exception)
{
throw;
}
return ret;
}
public bool UpdateProduct(Product pr)
{
bool ret = false;
try
{
string sql = "Update Products set [email protected],"
+
"[email protected],[email protected],[email protected]," +
"[email protected],[email protected]," +
"[email protected] where [email protected]";
List<SqlParameter> PList = new
List<SqlParameter>();
DBHelper.AddSqlParam(PList, "@ProductId",
SqlDbType.Int,
pr.ProductId);
DBHelper.AddSqlParam(PList, "@ProductName",
SqlDbType.VarChar,
pr.ProductName, 50);
DBHelper.AddSqlParam(PList, "@Description",
SqlDbType.Text,
pr.Description);
DBHelper.AddSqlParam(PList, "@Price",
SqlDbType.Money, pr.Price);
DBHelper.AddSqlParam(PList, "@StockLevel",
SqlDbType.Int,
pr.StockLevel);
DBHelper.AddSqlParam(PList, "@CategoryId",
SqlDbType.Int,
pr.CategoryId);
DBHelper.AddSqlParam(PList, "@OnSale",
SqlDbType.Bit, pr.OnSale);
DBHelper.AddSqlParam(PList, "@Discontinued",
SqlDbType.Bit,
pr.Discontinued);
int rowsModified =
_dataAccess.InsertUpdateDelete(sql, PList);
if (rowsModified > 0)
ret = true;
7
}
catch (Exception)
{
throw;
}
return ret;
}
public Product GetProduct(int pid)
{
Product pr = null;
try
{
string sql = "select * from Products where
[email protected]";
List<SqlParameter> PList = new
List<SqlParameter>();
DBHelper.AddSqlParam(PList, "@ProductId",
SqlDbType.Int, pid);
DataTable dt = _dataAccess.GetManyRowsCols(sql,
PList);
if (dt.Rows.Count > 0)
{
DataRow dr = dt.Rows[0];
pr = new Product();
pr.ProductId = (int)dr["ProductId"];
pr.ProductName = (string)dr["ProductName"];
pr.Description = (string)dr["Description"];
pr.Price = (decimal)dr["Price"];
pr.StockLevel = (int)dr["StockLevel"];
pr.CategoryId = (int)dr["CategoryId"];
pr.OnSale = (bool)dr["OnSale"];
pr.Discontinued = (bool)dr["Discontinued"];
}
}
catch (Exception)
{
throw;
}
return pr;
}
public bool DeleteProduct(int pid)
{
bool ret = false;
try
{
string sql = "Delete from Products where
[email protected]";
List<SqlParameter> ParamList = new
List<SqlParameter>();
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
{
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();
// 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:
@model ProductsApp.Models.ProductCatVM
@{
ViewBag.Title = "ShowProducts";
}
h2>Show Products</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<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">
@{
Html.RenderPartial("_ShowProducts",
Model.PList);
}
</div>
</div>
</div>
}
@section Scripts
{
<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>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.ProductId)
</th>
<th>
@Html.DisplayNameFor(model =>
model.ProductName)
</th>
<th>
@Html.DisplayNameFor(model => model.Description)
</th>
<th>
@Html.DisplayNameFor(model => model.Price)
</th>
<th>
@Html.DisplayNameFor(model => model.StockLevel)
</th>
<th>
@Html.DisplayNameFor(model => model.OnSale)
</th>
<th>
@Html.DisplayNameFor(model => model.Discontinued)
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.ProductId)
</td>
<td>
@Html.DisplayFor(modelItem => item.ProductName)
</td>
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.
<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.
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("$&nbsp;", 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
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>
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>
}
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
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>
@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);
});
}
</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
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
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.
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);
}
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
@{
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>
}
<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..");
$.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
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);
}
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
{
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.
@model ProductsApp.Models.ProductCatVM
@{
ViewBag.Title = "ShowProductsAF";
}
<h2>ShowProducts</h2>
@Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-
danger" })
<div class="row">
<div class="col-md-2">
23
@using (Ajax.BeginForm("GetProductsAF", "Products",
Model.categoryIdSelected, new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "Get",
OnFailure = "ProductsLoadFailed", // javascript
function
OnSuccess = "ProductsLoadOK",
AllowCache = false,
LoadingElementId = "divLoading",
UpdateTargetId = "divProducts", // PartialView will
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();
});
})
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>
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
{
<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
{
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"));
bundles.Add(new
ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.validate*"));
bundles.Add(new
ScriptBundle("~/bundles/jqueryui").Include(
"~/Scripts/jquery-ui*"));
bundles.Add(new
ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/modernizr-*"));
bundles.Add(new
ScriptBundle("~/bundles/bootstrap").Include(
"~/Scripts/bootstrap.js",
"~/Scripts/respond.js"));
bundles.Add(new
StyleBundle("~/Content/css").Include(
"~/Content/bootstrap.css",
"~/Content/themes/base/jquery-ui.min.css", //
important for modal popup
"~/Content/site.css"));
}
Add the following actions to the Products controller.
public ActionResult ProductsModal() // displays cat
dropdown and products
{
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
{
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
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(),
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;
// 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
};
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
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)
</th>
<th>
@Html.DisplayNameFor(model =>
model.PList[0].Description)
</th>
<th>
@Html.DisplayNameFor(model =>
model.PList[0].Price)
</th>
<th>
@Html.DisplayNameFor(model =>
model.PList[0].StockLevel)
</th>
<th>
@Html.DisplayNameFor(model =>
model.PList[0].OnSale)
</th>
<th>
@Html.DisplayNameFor(model =>
model.PList[0].Discontinued)
</th>
31
<th></th>
</tr>
@foreach (var item in Model.PList)
{
<tr>
<td>
@Html.DisplayFor(modelItem =>
item.ProductId)
</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>
</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' })
$("#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");
}
},
"modal":true
});
e.preventDefault();
return false;
});
//------------------------------------------------------
//-------------------Edit Modal Processing------------------
---
var DialogEdit = function () {
this.showDialog = function (url) {
$("#EditDialogDiv").dialog({
autoOpen: true,
width: "600",
resizable: false,
title: "Edit Product",
modal: true,
closeText: "X",
dialogClass: 'alert',
position: {
my: "center",
at: "top+150"
},
closeOnEscape: true,
33
open: function () {
$(this).load(url);
},
buttons: {
"Edit Product": function () {
editProductProcessing(url); // javascript
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-
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.
@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>
<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) {
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:
@model ProductsApp.Models.ProductCreateEditVM
@{
ViewBag.Title = "CreateProduct";
}
<h2>Create New Product</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
36
<div class="form-horizontal">
<h4>Product</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-
danger" })
<div class="form-group">
@Html.LabelFor(model => model.Prod.ProductId,
htmlAttributes: new {
@class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Prod.ProductId,
new { htmlAttributes =
new { @class = "form-control" } })
@Html.ValidationMessageFor(model =>
model.Prod.ProductId, "", new {
@class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Prod.ProductName,
htmlAttributes: new {
@class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model =>
model.Prod.ProductName, new { htmlAttributes
= new { @class = "form-control" } })
@Html.ValidationMessageFor(model =>
model.Prod.ProductName, "", new {
@class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Prod.Description,
htmlAttributes: new {
@class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Prod.Description,
new { htmlAttributes
= new { @class = "form-control" } })
@Html.ValidationMessageFor(model =>
model.Prod.Description, "", new {
@class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Prod.Price,
htmlAttributes: new { @class =
"control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Prod.Price, new {
htmlAttributes = new
{ @class = "form-control" } })
@Html.ValidationMessageFor(model =>
model.Prod.Price, "", new {
@class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Prod.StockLevel,
htmlAttributes: new {
@class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Prod.StockLevel,
new { htmlAttributes
= new { @class = "form-control" } })
37
@Html.ValidationMessageFor(model =>
model.Prod.StockLevel, "", new {
@class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.Label("Category", htmlAttributes: new { @class
= "control-label
col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(model =>
model.CategoryIdSelected,
Model.CatList, new { htmlAttributes = new { @class = "form-
control" } })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Prod.OnSale,
htmlAttributes: new { @class =
"control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
@Html.CheckBoxFor(model =>
model.Prod.OnSale)
@Html.ValidationMessageFor(model =>
model.Prod.OnSale, "", new {
@class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Prod.Discontinued,
htmlAttributes: new {
@class = "control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
@Html.CheckBoxFor(model =>
model.Prod.Discontinued)
@Html.ValidationMessageFor(model =>
model.Prod.Discontinued, "",
new { @class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create Product"
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
@{
ViewBag.Title = "Edit Product";
Layout = null;
}
<h2>Edit Product</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Product</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-
danger" })
<div class="form-group">
@Html.LabelFor(model => model.Prod.ProductId,
htmlAttributes: new {
@class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Prod.ProductId,
new { htmlAttributes =
new { @class = "form-control" } })
@Html.ValidationMessageFor(model =>
model.Prod.ProductId, "", new {
@class = "text-danger" })
</div>
</div>
39
<div class="form-group">
@Html.LabelFor(model => model.Prod.ProductName,
htmlAttributes: new {
@class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model =>
model.Prod.ProductName, new { htmlAttributes
= new { @class = "form-control" } })
@Html.ValidationMessageFor(model =>
model.Prod.ProductName, "", new {
@class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Prod.Description,
htmlAttributes: new {
@class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Prod.Description,
new { htmlAttributes
= new { @class = "form-control" } })
@Html.ValidationMessageFor(model =>
model.Prod.Description, "", new {
@class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Prod.Price,
htmlAttributes: new { @class =
"control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Prod.Price, new {
htmlAttributes = new
{ @class = "form-control" } })
@Html.ValidationMessageFor(model =>
model.Prod.Price, "", new {
@class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Prod.StockLevel,
htmlAttributes: new {
@class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Prod.StockLevel,
new { htmlAttributes
= new { @class = "form-control" } })
@Html.ValidationMessageFor(model =>
model.Prod.StockLevel, "", new {
@class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.Label("Category", htmlAttributes: new { @class
= "control-label
col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(model =>
model.CategoryIdSelected,
Model.CatList, new { htmlAttributes = new { @class = "form-
control" } })
</div>
</div>
<div class="form-group">
40
@Html.LabelFor(model => model.Prod.OnSale,
htmlAttributes: new { @class =
"control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
@Html.CheckBoxFor(model =>
model.Prod.OnSale)
@Html.ValidationMessageFor(model =>
model.Prod.OnSale, "", new {
@class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Prod.Discontinued,
htmlAttributes: new {
@class = "control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
@Html.CheckBoxFor(model =>
model.Prod.Discontinued)
@Html.ValidationMessageFor(model =>
model.Prod.Discontinued, "",
new { @class = "text-danger" })
</div>
</div>
</div>
</div>
<div>@ViewBag.Status</div>
<div id="divStatus"></div>
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
Build and test the creating, editing of a product via the Products
Modal link.
41

More Related Content

Similar to 1 MVC – Ajax and Modal Views AJAX stands for Asynch.docx

ASP.NET Web API
ASP.NET Web APIASP.NET Web API
ASP.NET Web APIhabib_786
 
Accessing data with android cursors
Accessing data with android cursorsAccessing data with android cursors
Accessing data with android cursorsinfo_zybotech
 
Accessing data with android cursors
Accessing data with android cursorsAccessing data with android cursors
Accessing data with android cursorsinfo_zybotech
 
ASP.NET 08 - Data Binding And Representation
ASP.NET 08 - Data Binding And RepresentationASP.NET 08 - Data Binding And Representation
ASP.NET 08 - Data Binding And RepresentationRandy Connolly
 
Summer - The HTML5 Library for Java and Scala
Summer - The HTML5 Library for Java and ScalaSummer - The HTML5 Library for Java and Scala
Summer - The HTML5 Library for Java and Scalarostislav
 
Intake 38 data access 5
Intake 38 data access 5Intake 38 data access 5
Intake 38 data access 5Mahmoud Ouf
 
ACADGILD:: ANDROID LESSON
ACADGILD:: ANDROID LESSON ACADGILD:: ANDROID LESSON
ACADGILD:: ANDROID LESSON Padma shree. T
 
VISUALIZAR REGISTROS EN UN JTABLE
VISUALIZAR REGISTROS EN UN JTABLEVISUALIZAR REGISTROS EN UN JTABLE
VISUALIZAR REGISTROS EN UN JTABLEDarwin Durand
 
Session06 handling xml data
Session06  handling xml dataSession06  handling xml data
Session06 handling xml datakendyhuu
 
Taming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, MacoscopeTaming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, MacoscopeMacoscope
 
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...탑크리에듀(구로디지털단지역3번출구 2분거리)
 
QTP Automation Testing Tutorial 7
QTP Automation Testing Tutorial 7QTP Automation Testing Tutorial 7
QTP Automation Testing Tutorial 7Akash Tyagi
 
Powerful and flexible templates with Twig
Powerful and flexible templates with Twig Powerful and flexible templates with Twig
Powerful and flexible templates with Twig Michael Peacock
 
I am looking for some assistance with SQLite database. I have tried se.pdf
I am looking for some assistance with SQLite database. I have tried se.pdfI am looking for some assistance with SQLite database. I have tried se.pdf
I am looking for some assistance with SQLite database. I have tried se.pdfConint29
 

Similar to 1 MVC – Ajax and Modal Views AJAX stands for Asynch.docx (20)

ASP.NET Web API
ASP.NET Web APIASP.NET Web API
ASP.NET Web API
 
Accessing data with android cursors
Accessing data with android cursorsAccessing data with android cursors
Accessing data with android cursors
 
Accessing data with android cursors
Accessing data with android cursorsAccessing data with android cursors
Accessing data with android cursors
 
ASP.NET 08 - Data Binding And Representation
ASP.NET 08 - Data Binding And RepresentationASP.NET 08 - Data Binding And Representation
ASP.NET 08 - Data Binding And Representation
 
Summer - The HTML5 Library for Java and Scala
Summer - The HTML5 Library for Java and ScalaSummer - The HTML5 Library for Java and Scala
Summer - The HTML5 Library for Java and Scala
 
Intake 38 data access 5
Intake 38 data access 5Intake 38 data access 5
Intake 38 data access 5
 
ACADGILD:: ANDROID LESSON
ACADGILD:: ANDROID LESSON ACADGILD:: ANDROID LESSON
ACADGILD:: ANDROID LESSON
 
VISUALIZAR REGISTROS EN UN JTABLE
VISUALIZAR REGISTROS EN UN JTABLEVISUALIZAR REGISTROS EN UN JTABLE
VISUALIZAR REGISTROS EN UN JTABLE
 
Android sq lite-chapter 22
Android sq lite-chapter 22Android sq lite-chapter 22
Android sq lite-chapter 22
 
Session 2- day 3
Session 2- day 3Session 2- day 3
Session 2- day 3
 
Session06 handling xml data
Session06  handling xml dataSession06  handling xml data
Session06 handling xml data
 
Taming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, MacoscopeTaming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, Macoscope
 
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
#18.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
 
Sql server-function
Sql server-functionSql server-function
Sql server-function
 
Practica n° 7
Practica n° 7Practica n° 7
Practica n° 7
 
QTP Automation Testing Tutorial 7
QTP Automation Testing Tutorial 7QTP Automation Testing Tutorial 7
QTP Automation Testing Tutorial 7
 
Lecture13
Lecture13Lecture13
Lecture13
 
Powerful and flexible templates with Twig
Powerful and flexible templates with Twig Powerful and flexible templates with Twig
Powerful and flexible templates with Twig
 
JPA 2.0
JPA 2.0JPA 2.0
JPA 2.0
 
I am looking for some assistance with SQLite database. I have tried se.pdf
I am looking for some assistance with SQLite database. I have tried se.pdfI am looking for some assistance with SQLite database. I have tried se.pdf
I am looking for some assistance with SQLite database. I have tried se.pdf
 

More from honey725342

NRS-493 Individual Success PlanREQUIRED PRACTICE HOURS 100 Direct.docx
NRS-493 Individual Success PlanREQUIRED PRACTICE HOURS 100 Direct.docxNRS-493 Individual Success PlanREQUIRED PRACTICE HOURS 100 Direct.docx
NRS-493 Individual Success PlanREQUIRED PRACTICE HOURS 100 Direct.docxhoney725342
 
Now the Earth has had wide variations in atmospheric CO2-level throu.docx
Now the Earth has had wide variations in atmospheric CO2-level throu.docxNow the Earth has had wide variations in atmospheric CO2-level throu.docx
Now the Earth has had wide variations in atmospheric CO2-level throu.docxhoney725342
 
NR224 Fundamentals SkillsTopic Safety Goals BOOK P.docx
NR224 Fundamentals SkillsTopic Safety Goals BOOK P.docxNR224 Fundamentals SkillsTopic Safety Goals BOOK P.docx
NR224 Fundamentals SkillsTopic Safety Goals BOOK P.docxhoney725342
 
Nurse Education Today 87 (2020) 104348Contents lists avail.docx
Nurse Education Today 87 (2020) 104348Contents lists avail.docxNurse Education Today 87 (2020) 104348Contents lists avail.docx
Nurse Education Today 87 (2020) 104348Contents lists avail.docxhoney725342
 
Now that you’ve seen all of the elements contributing to the Devil’s.docx
Now that you’ve seen all of the elements contributing to the Devil’s.docxNow that you’ve seen all of the elements contributing to the Devil’s.docx
Now that you’ve seen all of the elements contributing to the Devil’s.docxhoney725342
 
NR360 We Can But Dare We.docx Revised 5 ‐ 9 .docx
NR360   We   Can   But   Dare   We.docx   Revised   5 ‐ 9 .docxNR360   We   Can   But   Dare   We.docx   Revised   5 ‐ 9 .docx
NR360 We Can But Dare We.docx Revised 5 ‐ 9 .docxhoney725342
 
Nurse Practitioner Diagnosis- Chest Pain.SOAPS-Subjective.docx
Nurse Practitioner Diagnosis- Chest Pain.SOAPS-Subjective.docxNurse Practitioner Diagnosis- Chest Pain.SOAPS-Subjective.docx
Nurse Practitioner Diagnosis- Chest Pain.SOAPS-Subjective.docxhoney725342
 
NURS 6002 Foundations of Graduate StudyAcademic and P.docx
NURS 6002 Foundations of Graduate StudyAcademic and P.docxNURS 6002 Foundations of Graduate StudyAcademic and P.docx
NURS 6002 Foundations of Graduate StudyAcademic and P.docxhoney725342
 
Nurse workforce shortage are predicted to get worse as baby boomers .docx
Nurse workforce shortage are predicted to get worse as baby boomers .docxNurse workforce shortage are predicted to get worse as baby boomers .docx
Nurse workforce shortage are predicted to get worse as baby boomers .docxhoney725342
 
Now, for the exam itself. Below are 4 questions. You need to answer .docx
Now, for the exam itself. Below are 4 questions. You need to answer .docxNow, for the exam itself. Below are 4 questions. You need to answer .docx
Now, for the exam itself. Below are 4 questions. You need to answer .docxhoney725342
 
Nur-501-AP4- Philosophical and Theoretical Evidence-Based research.docx
Nur-501-AP4- Philosophical and Theoretical Evidence-Based research.docxNur-501-AP4- Philosophical and Theoretical Evidence-Based research.docx
Nur-501-AP4- Philosophical and Theoretical Evidence-Based research.docxhoney725342
 
NU32CH19-Foltz ARI 9 July 2012 1945Population-Level Inter.docx
NU32CH19-Foltz ARI 9 July 2012 1945Population-Level Inter.docxNU32CH19-Foltz ARI 9 July 2012 1945Population-Level Inter.docx
NU32CH19-Foltz ARI 9 July 2012 1945Population-Level Inter.docxhoney725342
 
Nurse Working in the CommunityDescribe the community nurses.docx
Nurse Working in the CommunityDescribe the community nurses.docxNurse Working in the CommunityDescribe the community nurses.docx
Nurse Working in the CommunityDescribe the community nurses.docxhoney725342
 
nursing diagnosis1. Decreased Cardiac Output  related to Alter.docx
nursing diagnosis1. Decreased Cardiac Output  related to Alter.docxnursing diagnosis1. Decreased Cardiac Output  related to Alter.docx
nursing diagnosis1. Decreased Cardiac Output  related to Alter.docxhoney725342
 
Nursing Documentation Is it valuable Discuss the value of nursin.docx
Nursing Documentation Is it valuable Discuss the value of nursin.docxNursing Documentation Is it valuable Discuss the value of nursin.docx
Nursing Documentation Is it valuable Discuss the value of nursin.docxhoney725342
 
NR631 Concluding Graduate Experience - Scope Project Managemen.docx
NR631 Concluding Graduate Experience - Scope  Project Managemen.docxNR631 Concluding Graduate Experience - Scope  Project Managemen.docx
NR631 Concluding Graduate Experience - Scope Project Managemen.docxhoney725342
 
Number 11. Describe at least five populations who are vulner.docx
Number 11. Describe at least five populations who are vulner.docxNumber 11. Describe at least five populations who are vulner.docx
Number 11. Describe at least five populations who are vulner.docxhoney725342
 
ntertainment, the media, and sometimes public leaders can perpetuate.docx
ntertainment, the media, and sometimes public leaders can perpetuate.docxntertainment, the media, and sometimes public leaders can perpetuate.docx
ntertainment, the media, and sometimes public leaders can perpetuate.docxhoney725342
 
Now that you have  completed Lesson 23 & 24 and have thought a.docx
Now that you have  completed Lesson 23 & 24 and have thought a.docxNow that you have  completed Lesson 23 & 24 and have thought a.docx
Now that you have  completed Lesson 23 & 24 and have thought a.docxhoney725342
 
nothing wrong with the paper, my professor just wants it to be in an.docx
nothing wrong with the paper, my professor just wants it to be in an.docxnothing wrong with the paper, my professor just wants it to be in an.docx
nothing wrong with the paper, my professor just wants it to be in an.docxhoney725342
 

More from honey725342 (20)

NRS-493 Individual Success PlanREQUIRED PRACTICE HOURS 100 Direct.docx
NRS-493 Individual Success PlanREQUIRED PRACTICE HOURS 100 Direct.docxNRS-493 Individual Success PlanREQUIRED PRACTICE HOURS 100 Direct.docx
NRS-493 Individual Success PlanREQUIRED PRACTICE HOURS 100 Direct.docx
 
Now the Earth has had wide variations in atmospheric CO2-level throu.docx
Now the Earth has had wide variations in atmospheric CO2-level throu.docxNow the Earth has had wide variations in atmospheric CO2-level throu.docx
Now the Earth has had wide variations in atmospheric CO2-level throu.docx
 
NR224 Fundamentals SkillsTopic Safety Goals BOOK P.docx
NR224 Fundamentals SkillsTopic Safety Goals BOOK P.docxNR224 Fundamentals SkillsTopic Safety Goals BOOK P.docx
NR224 Fundamentals SkillsTopic Safety Goals BOOK P.docx
 
Nurse Education Today 87 (2020) 104348Contents lists avail.docx
Nurse Education Today 87 (2020) 104348Contents lists avail.docxNurse Education Today 87 (2020) 104348Contents lists avail.docx
Nurse Education Today 87 (2020) 104348Contents lists avail.docx
 
Now that you’ve seen all of the elements contributing to the Devil’s.docx
Now that you’ve seen all of the elements contributing to the Devil’s.docxNow that you’ve seen all of the elements contributing to the Devil’s.docx
Now that you’ve seen all of the elements contributing to the Devil’s.docx
 
NR360 We Can But Dare We.docx Revised 5 ‐ 9 .docx
NR360   We   Can   But   Dare   We.docx   Revised   5 ‐ 9 .docxNR360   We   Can   But   Dare   We.docx   Revised   5 ‐ 9 .docx
NR360 We Can But Dare We.docx Revised 5 ‐ 9 .docx
 
Nurse Practitioner Diagnosis- Chest Pain.SOAPS-Subjective.docx
Nurse Practitioner Diagnosis- Chest Pain.SOAPS-Subjective.docxNurse Practitioner Diagnosis- Chest Pain.SOAPS-Subjective.docx
Nurse Practitioner Diagnosis- Chest Pain.SOAPS-Subjective.docx
 
NURS 6002 Foundations of Graduate StudyAcademic and P.docx
NURS 6002 Foundations of Graduate StudyAcademic and P.docxNURS 6002 Foundations of Graduate StudyAcademic and P.docx
NURS 6002 Foundations of Graduate StudyAcademic and P.docx
 
Nurse workforce shortage are predicted to get worse as baby boomers .docx
Nurse workforce shortage are predicted to get worse as baby boomers .docxNurse workforce shortage are predicted to get worse as baby boomers .docx
Nurse workforce shortage are predicted to get worse as baby boomers .docx
 
Now, for the exam itself. Below are 4 questions. You need to answer .docx
Now, for the exam itself. Below are 4 questions. You need to answer .docxNow, for the exam itself. Below are 4 questions. You need to answer .docx
Now, for the exam itself. Below are 4 questions. You need to answer .docx
 
Nur-501-AP4- Philosophical and Theoretical Evidence-Based research.docx
Nur-501-AP4- Philosophical and Theoretical Evidence-Based research.docxNur-501-AP4- Philosophical and Theoretical Evidence-Based research.docx
Nur-501-AP4- Philosophical and Theoretical Evidence-Based research.docx
 
NU32CH19-Foltz ARI 9 July 2012 1945Population-Level Inter.docx
NU32CH19-Foltz ARI 9 July 2012 1945Population-Level Inter.docxNU32CH19-Foltz ARI 9 July 2012 1945Population-Level Inter.docx
NU32CH19-Foltz ARI 9 July 2012 1945Population-Level Inter.docx
 
Nurse Working in the CommunityDescribe the community nurses.docx
Nurse Working in the CommunityDescribe the community nurses.docxNurse Working in the CommunityDescribe the community nurses.docx
Nurse Working in the CommunityDescribe the community nurses.docx
 
nursing diagnosis1. Decreased Cardiac Output  related to Alter.docx
nursing diagnosis1. Decreased Cardiac Output  related to Alter.docxnursing diagnosis1. Decreased Cardiac Output  related to Alter.docx
nursing diagnosis1. Decreased Cardiac Output  related to Alter.docx
 
Nursing Documentation Is it valuable Discuss the value of nursin.docx
Nursing Documentation Is it valuable Discuss the value of nursin.docxNursing Documentation Is it valuable Discuss the value of nursin.docx
Nursing Documentation Is it valuable Discuss the value of nursin.docx
 
NR631 Concluding Graduate Experience - Scope Project Managemen.docx
NR631 Concluding Graduate Experience - Scope  Project Managemen.docxNR631 Concluding Graduate Experience - Scope  Project Managemen.docx
NR631 Concluding Graduate Experience - Scope Project Managemen.docx
 
Number 11. Describe at least five populations who are vulner.docx
Number 11. Describe at least five populations who are vulner.docxNumber 11. Describe at least five populations who are vulner.docx
Number 11. Describe at least five populations who are vulner.docx
 
ntertainment, the media, and sometimes public leaders can perpetuate.docx
ntertainment, the media, and sometimes public leaders can perpetuate.docxntertainment, the media, and sometimes public leaders can perpetuate.docx
ntertainment, the media, and sometimes public leaders can perpetuate.docx
 
Now that you have  completed Lesson 23 & 24 and have thought a.docx
Now that you have  completed Lesson 23 & 24 and have thought a.docxNow that you have  completed Lesson 23 & 24 and have thought a.docx
Now that you have  completed Lesson 23 & 24 and have thought a.docx
 
nothing wrong with the paper, my professor just wants it to be in an.docx
nothing wrong with the paper, my professor just wants it to be in an.docxnothing wrong with the paper, my professor just wants it to be in an.docx
nothing wrong with the paper, my professor just wants it to be in an.docx
 

Recently uploaded

1029-Danh muc Sach Giao Khoa khoi 6.pdf
1029-Danh muc Sach Giao Khoa khoi  6.pdf1029-Danh muc Sach Giao Khoa khoi  6.pdf
1029-Danh muc Sach Giao Khoa khoi 6.pdfQucHHunhnh
 
Web & Social Media Analytics Previous Year Question Paper.pdf
Web & Social Media Analytics Previous Year Question Paper.pdfWeb & Social Media Analytics Previous Year Question Paper.pdf
Web & Social Media Analytics Previous Year Question Paper.pdfJayanti Pande
 
Grant Readiness 101 TechSoup and Remy Consulting
Grant Readiness 101 TechSoup and Remy ConsultingGrant Readiness 101 TechSoup and Remy Consulting
Grant Readiness 101 TechSoup and Remy ConsultingTechSoup
 
1029 - Danh muc Sach Giao Khoa 10 . pdf
1029 -  Danh muc Sach Giao Khoa 10 . pdf1029 -  Danh muc Sach Giao Khoa 10 . pdf
1029 - Danh muc Sach Giao Khoa 10 . pdfQucHHunhnh
 
Z Score,T Score, Percential Rank and Box Plot Graph
Z Score,T Score, Percential Rank and Box Plot GraphZ Score,T Score, Percential Rank and Box Plot Graph
Z Score,T Score, Percential Rank and Box Plot GraphThiyagu K
 
social pharmacy d-pharm 1st year by Pragati K. Mahajan
social pharmacy d-pharm 1st year by Pragati K. Mahajansocial pharmacy d-pharm 1st year by Pragati K. Mahajan
social pharmacy d-pharm 1st year by Pragati K. Mahajanpragatimahajan3
 
Measures of Central Tendency: Mean, Median and Mode
Measures of Central Tendency: Mean, Median and ModeMeasures of Central Tendency: Mean, Median and Mode
Measures of Central Tendency: Mean, Median and ModeThiyagu K
 
JAPAN: ORGANISATION OF PMDA, PHARMACEUTICAL LAWS & REGULATIONS, TYPES OF REGI...
JAPAN: ORGANISATION OF PMDA, PHARMACEUTICAL LAWS & REGULATIONS, TYPES OF REGI...JAPAN: ORGANISATION OF PMDA, PHARMACEUTICAL LAWS & REGULATIONS, TYPES OF REGI...
JAPAN: ORGANISATION OF PMDA, PHARMACEUTICAL LAWS & REGULATIONS, TYPES OF REGI...anjaliyadav012327
 
Software Engineering Methodologies (overview)
Software Engineering Methodologies (overview)Software Engineering Methodologies (overview)
Software Engineering Methodologies (overview)eniolaolutunde
 
The basics of sentences session 2pptx copy.pptx
The basics of sentences session 2pptx copy.pptxThe basics of sentences session 2pptx copy.pptx
The basics of sentences session 2pptx copy.pptxheathfieldcps1
 
Sports & Fitness Value Added Course FY..
Sports & Fitness Value Added Course FY..Sports & Fitness Value Added Course FY..
Sports & Fitness Value Added Course FY..Disha Kariya
 
BAG TECHNIQUE Bag technique-a tool making use of public health bag through wh...
BAG TECHNIQUE Bag technique-a tool making use of public health bag through wh...BAG TECHNIQUE Bag technique-a tool making use of public health bag through wh...
BAG TECHNIQUE Bag technique-a tool making use of public health bag through wh...Sapna Thakur
 
Sanyam Choudhary Chemistry practical.pdf
Sanyam Choudhary Chemistry practical.pdfSanyam Choudhary Chemistry practical.pdf
Sanyam Choudhary Chemistry practical.pdfsanyamsingh5019
 
Arihant handbook biology for class 11 .pdf
Arihant handbook biology for class 11 .pdfArihant handbook biology for class 11 .pdf
Arihant handbook biology for class 11 .pdfchloefrazer622
 
Organic Name Reactions for the students and aspirants of Chemistry12th.pptx
Organic Name Reactions  for the students and aspirants of Chemistry12th.pptxOrganic Name Reactions  for the students and aspirants of Chemistry12th.pptx
Organic Name Reactions for the students and aspirants of Chemistry12th.pptxVS Mahajan Coaching Centre
 
Paris 2024 Olympic Geographies - an activity
Paris 2024 Olympic Geographies - an activityParis 2024 Olympic Geographies - an activity
Paris 2024 Olympic Geographies - an activityGeoBlogs
 
Activity 01 - Artificial Culture (1).pdf
Activity 01 - Artificial Culture (1).pdfActivity 01 - Artificial Culture (1).pdf
Activity 01 - Artificial Culture (1).pdfciinovamais
 

Recently uploaded (20)

1029-Danh muc Sach Giao Khoa khoi 6.pdf
1029-Danh muc Sach Giao Khoa khoi  6.pdf1029-Danh muc Sach Giao Khoa khoi  6.pdf
1029-Danh muc Sach Giao Khoa khoi 6.pdf
 
Web & Social Media Analytics Previous Year Question Paper.pdf
Web & Social Media Analytics Previous Year Question Paper.pdfWeb & Social Media Analytics Previous Year Question Paper.pdf
Web & Social Media Analytics Previous Year Question Paper.pdf
 
Grant Readiness 101 TechSoup and Remy Consulting
Grant Readiness 101 TechSoup and Remy ConsultingGrant Readiness 101 TechSoup and Remy Consulting
Grant Readiness 101 TechSoup and Remy Consulting
 
1029 - Danh muc Sach Giao Khoa 10 . pdf
1029 -  Danh muc Sach Giao Khoa 10 . pdf1029 -  Danh muc Sach Giao Khoa 10 . pdf
1029 - Danh muc Sach Giao Khoa 10 . pdf
 
Z Score,T Score, Percential Rank and Box Plot Graph
Z Score,T Score, Percential Rank and Box Plot GraphZ Score,T Score, Percential Rank and Box Plot Graph
Z Score,T Score, Percential Rank and Box Plot Graph
 
social pharmacy d-pharm 1st year by Pragati K. Mahajan
social pharmacy d-pharm 1st year by Pragati K. Mahajansocial pharmacy d-pharm 1st year by Pragati K. Mahajan
social pharmacy d-pharm 1st year by Pragati K. Mahajan
 
Measures of Central Tendency: Mean, Median and Mode
Measures of Central Tendency: Mean, Median and ModeMeasures of Central Tendency: Mean, Median and Mode
Measures of Central Tendency: Mean, Median and Mode
 
JAPAN: ORGANISATION OF PMDA, PHARMACEUTICAL LAWS & REGULATIONS, TYPES OF REGI...
JAPAN: ORGANISATION OF PMDA, PHARMACEUTICAL LAWS & REGULATIONS, TYPES OF REGI...JAPAN: ORGANISATION OF PMDA, PHARMACEUTICAL LAWS & REGULATIONS, TYPES OF REGI...
JAPAN: ORGANISATION OF PMDA, PHARMACEUTICAL LAWS & REGULATIONS, TYPES OF REGI...
 
Software Engineering Methodologies (overview)
Software Engineering Methodologies (overview)Software Engineering Methodologies (overview)
Software Engineering Methodologies (overview)
 
The basics of sentences session 2pptx copy.pptx
The basics of sentences session 2pptx copy.pptxThe basics of sentences session 2pptx copy.pptx
The basics of sentences session 2pptx copy.pptx
 
Advance Mobile Application Development class 07
Advance Mobile Application Development class 07Advance Mobile Application Development class 07
Advance Mobile Application Development class 07
 
Sports & Fitness Value Added Course FY..
Sports & Fitness Value Added Course FY..Sports & Fitness Value Added Course FY..
Sports & Fitness Value Added Course FY..
 
BAG TECHNIQUE Bag technique-a tool making use of public health bag through wh...
BAG TECHNIQUE Bag technique-a tool making use of public health bag through wh...BAG TECHNIQUE Bag technique-a tool making use of public health bag through wh...
BAG TECHNIQUE Bag technique-a tool making use of public health bag through wh...
 
Sanyam Choudhary Chemistry practical.pdf
Sanyam Choudhary Chemistry practical.pdfSanyam Choudhary Chemistry practical.pdf
Sanyam Choudhary Chemistry practical.pdf
 
Arihant handbook biology for class 11 .pdf
Arihant handbook biology for class 11 .pdfArihant handbook biology for class 11 .pdf
Arihant handbook biology for class 11 .pdf
 
Organic Name Reactions for the students and aspirants of Chemistry12th.pptx
Organic Name Reactions  for the students and aspirants of Chemistry12th.pptxOrganic Name Reactions  for the students and aspirants of Chemistry12th.pptx
Organic Name Reactions for the students and aspirants of Chemistry12th.pptx
 
Paris 2024 Olympic Geographies - an activity
Paris 2024 Olympic Geographies - an activityParis 2024 Olympic Geographies - an activity
Paris 2024 Olympic Geographies - an activity
 
INDIA QUIZ 2024 RLAC DELHI UNIVERSITY.pptx
INDIA QUIZ 2024 RLAC DELHI UNIVERSITY.pptxINDIA QUIZ 2024 RLAC DELHI UNIVERSITY.pptx
INDIA QUIZ 2024 RLAC DELHI UNIVERSITY.pptx
 
Mattingly "AI & Prompt Design: Structured Data, Assistants, & RAG"
Mattingly "AI & Prompt Design: Structured Data, Assistants, & RAG"Mattingly "AI & Prompt Design: Structured Data, Assistants, & RAG"
Mattingly "AI & Prompt Design: Structured Data, Assistants, & RAG"
 
Activity 01 - Artificial Culture (1).pdf
Activity 01 - Artificial Culture (1).pdfActivity 01 - Artificial Culture (1).pdf
Activity 01 - Artificial Culture (1).pdf
 

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," +
  • 10. "@ProductName,@Description,@Price,@StockLevel,@Categor yId,@OnSale," + "@Discontinued)"; 6 List<SqlParameter> PList = new List<SqlParameter>(); DBHelper.AddSqlParam(PList, "@ProductId", SqlDbType.Int, pr.ProductId); DBHelper.AddSqlParam(PList,"@ProductName", SqlDbType.VarChar, pr.ProductName,50); DBHelper.AddSqlParam(PList, "@Description", SqlDbType.Text, pr.Description); DBHelper.AddSqlParam(PList, "@Price", SqlDbType.Money, pr.Price); DBHelper.AddSqlParam(PList, "@StockLevel", SqlDbType.Int, pr.StockLevel); DBHelper.AddSqlParam(PList, "@CategoryId", SqlDbType.Int, pr.CategoryId); DBHelper.AddSqlParam(PList, "@OnSale", SqlDbType.Bit, pr.OnSale); DBHelper.AddSqlParam(PList, "@Discontinued", SqlDbType.Bit, pr.Discontinued); int rowsModified = _dataAccess.InsertUpdateDelete(sql, PList);
  • 11. if (rowsModified > 0) ret = true; } catch (Exception) { throw; } return ret; } public bool UpdateProduct(Product pr) { bool ret = false; try { string sql = "Update Products set [email protected]," + "[email protected],[email protected],[email protected]," + "[email protected],[email protected]," + "[email protected] where [email protected]"; List<SqlParameter> PList = new List<SqlParameter>(); DBHelper.AddSqlParam(PList, "@ProductId", SqlDbType.Int, pr.ProductId); DBHelper.AddSqlParam(PList, "@ProductName", SqlDbType.VarChar, pr.ProductName, 50); DBHelper.AddSqlParam(PList, "@Description", SqlDbType.Text, pr.Description); DBHelper.AddSqlParam(PList, "@Price", SqlDbType.Money, pr.Price); DBHelper.AddSqlParam(PList, "@StockLevel", SqlDbType.Int,
  • 12. pr.StockLevel); DBHelper.AddSqlParam(PList, "@CategoryId", SqlDbType.Int, pr.CategoryId); DBHelper.AddSqlParam(PList, "@OnSale", SqlDbType.Bit, pr.OnSale); DBHelper.AddSqlParam(PList, "@Discontinued", SqlDbType.Bit, pr.Discontinued); int rowsModified = _dataAccess.InsertUpdateDelete(sql, PList); if (rowsModified > 0) ret = true; 7 } catch (Exception) { throw; } return ret; } public Product GetProduct(int pid) { Product pr = null; try { string sql = "select * from Products where [email protected]"; List<SqlParameter> PList = new
  • 13. List<SqlParameter>(); DBHelper.AddSqlParam(PList, "@ProductId", SqlDbType.Int, pid); DataTable dt = _dataAccess.GetManyRowsCols(sql, PList); if (dt.Rows.Count > 0) { DataRow dr = dt.Rows[0]; pr = new Product(); pr.ProductId = (int)dr["ProductId"]; pr.ProductName = (string)dr["ProductName"]; pr.Description = (string)dr["Description"]; pr.Price = (decimal)dr["Price"]; pr.StockLevel = (int)dr["StockLevel"]; pr.CategoryId = (int)dr["CategoryId"]; pr.OnSale = (bool)dr["OnSale"]; pr.Discontinued = (bool)dr["Discontinued"]; } } catch (Exception) { throw; } return pr; } public bool DeleteProduct(int pid) { bool ret = false; try { string sql = "Delete from Products where [email protected]"; List<SqlParameter> ParamList = new List<SqlParameter>();
  • 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:
  • 17. @model ProductsApp.Models.ProductCatVM @{ ViewBag.Title = "ShowProducts"; } h2>Show Products</h2> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <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"> @{ Html.RenderPartial("_ShowProducts", Model.PList); } </div> </div> </div> } @section Scripts
  • 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>
  • 19. <table class="table"> <tr> <th> @Html.DisplayNameFor(model => model.ProductId) </th> <th> @Html.DisplayNameFor(model => model.ProductName) </th> <th> @Html.DisplayNameFor(model => model.Description) </th> <th> @Html.DisplayNameFor(model => model.Price) </th> <th> @Html.DisplayNameFor(model => model.StockLevel) </th> <th> @Html.DisplayNameFor(model => model.OnSale) </th> <th> @Html.DisplayNameFor(model => model.Discontinued) </th> <th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.ProductId) </td> <td> @Html.DisplayFor(modelItem => item.ProductName) </td>
  • 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("$&nbsp;", 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.
  • 39. @model ProductsApp.Models.ProductCatVM @{ ViewBag.Title = "ShowProductsAF"; } <h2>ShowProducts</h2> @Html.AntiForgeryToken() <div class="form-horizontal"> <hr /> @Html.ValidationSummary(true, "", new { @class = "text- danger" }) <div class="row"> <div class="col-md-2"> 23 @using (Ajax.BeginForm("GetProductsAF", "Products", Model.categoryIdSelected, new AjaxOptions { InsertionMode = InsertionMode.Replace, HttpMethod = "Get", OnFailure = "ProductsLoadFailed", // javascript function OnSuccess = "ProductsLoadOK", AllowCache = false, LoadingElementId = "divLoading", UpdateTargetId = "divProducts", // PartialView will
  • 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)
  • 53. </th> <th> @Html.DisplayNameFor(model => model.PList[0].Description) </th> <th> @Html.DisplayNameFor(model => model.PList[0].Price) </th> <th> @Html.DisplayNameFor(model => model.PList[0].StockLevel) </th> <th> @Html.DisplayNameFor(model => model.PList[0].OnSale) </th> <th> @Html.DisplayNameFor(model => model.PList[0].Discontinued) </th> 31 <th></th> </tr> @foreach (var item in Model.PList) { <tr> <td> @Html.DisplayFor(modelItem => item.ProductId)
  • 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"); } },
  • 57. "modal":true }); e.preventDefault(); return false; }); //------------------------------------------------------ //-------------------Edit Modal Processing------------------ --- var DialogEdit = function () { this.showDialog = function (url) { $("#EditDialogDiv").dialog({ autoOpen: true, width: "600", resizable: false, title: "Edit Product", modal: true, closeText: "X", dialogClass: 'alert', position: { my: "center", at: "top+150" }, closeOnEscape: true, 33 open: function () { $(this).load(url); }, buttons: { "Edit Product": function () { editProductProcessing(url); // javascript
  • 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:
  • 63. @model ProductsApp.Models.ProductCreateEditVM @{ ViewBag.Title = "CreateProduct"; } <h2>Create New Product</h2> @using (Html.BeginForm()) { @Html.AntiForgeryToken() 36 <div class="form-horizontal"> <h4>Product</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text- danger" }) <div class="form-group"> @Html.LabelFor(model => model.Prod.ProductId, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Prod.ProductId, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Prod.ProductId, "", new { @class = "text-danger" }) </div>
  • 64. </div> <div class="form-group"> @Html.LabelFor(model => model.Prod.ProductName, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Prod.ProductName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Prod.ProductName, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Prod.Description, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Prod.Description, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Prod.Description, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Prod.Price, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Prod.Price, new { htmlAttributes = new
  • 65. { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Prod.Price, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Prod.StockLevel, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Prod.StockLevel, new { htmlAttributes = new { @class = "form-control" } }) 37 @Html.ValidationMessageFor(model => model.Prod.StockLevel, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.Label("Category", htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.DropDownListFor(model => model.CategoryIdSelected, Model.CatList, new { htmlAttributes = new { @class = "form- control" } })
  • 66. </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Prod.OnSale, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> <div class="checkbox"> @Html.CheckBoxFor(model => model.Prod.OnSale) @Html.ValidationMessageFor(model => model.Prod.OnSale, "", new { @class = "text-danger" }) </div> </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Prod.Discontinued, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> <div class="checkbox"> @Html.CheckBoxFor(model => model.Prod.Discontinued) @Html.ValidationMessageFor(model => model.Prod.Discontinued, "", new { @class = "text-danger" }) </div> </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Create Product"
  • 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
  • 68. @{ ViewBag.Title = "Edit Product"; Layout = null; } <h2>Edit Product</h2> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>Product</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text- danger" }) <div class="form-group"> @Html.LabelFor(model => model.Prod.ProductId, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Prod.ProductId, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Prod.ProductId, "", new { @class = "text-danger" }) </div> </div> 39
  • 69. <div class="form-group"> @Html.LabelFor(model => model.Prod.ProductName, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Prod.ProductName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Prod.ProductName, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Prod.Description, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Prod.Description, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Prod.Description, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Prod.Price, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Prod.Price, new { htmlAttributes = new
  • 70. { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Prod.Price, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Prod.StockLevel, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Prod.StockLevel, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Prod.StockLevel, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.Label("Category", htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.DropDownListFor(model => model.CategoryIdSelected, Model.CatList, new { htmlAttributes = new { @class = "form- control" } }) </div> </div> <div class="form-group">
  • 71. 40 @Html.LabelFor(model => model.Prod.OnSale, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> <div class="checkbox"> @Html.CheckBoxFor(model => model.Prod.OnSale) @Html.ValidationMessageFor(model => model.Prod.OnSale, "", new { @class = "text-danger" }) </div> </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Prod.Discontinued, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> <div class="checkbox"> @Html.CheckBoxFor(model => model.Prod.Discontinued) @Html.ValidationMessageFor(model => model.Prod.Discontinued, "", new { @class = "text-danger" }) </div> </div> </div> </div> <div>@ViewBag.Status</div> <div id="divStatus"></div> }
  • 72. @section Scripts { @Scripts.Render("~/bundles/jqueryval") } Build and test the creating, editing of a product via the Products Modal link. 41