级联下拉列表和部分视图
使用JavaScript Voodoo在MVC中实现级联下拉列表以显示PartialViews。
引言
一篇短文,介绍了如何在MVC Razor页面中连接下拉列表,以动态更新并使用PartialViews填充页面。
背景
由于我刚接触MVC和Web开发,我很难理解JavaScript与我的控制器操作的交互。 在浏览了网络并问了许多愚蠢的问题之后,这是一个我拼凑并发现有用的简单解决方案。
如果您不了解MVC模式和/或Entity Framework,那么这篇文章可能不适合您,因为我不会深入解释如何连接这些。
使用代码
这里需要几件事。 我正在使用Entity Framework - Model First方法,因此我们需要设置一个简单的数据库。
这可以使用静态模型来完成,但这也很简单。 提供了一个脚本,但架构很简单:
Create Table ProductType(
Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
Name VarChar(50) UNIQUE NOT NULL
)
Create Table ProductCategory(
Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
ProductTypeId INT NOT NULL FOREIGN KEY REFERENCES ProductType(Id),
Name VARCHAR(50) NOT NULL ,
)
Create Table Product(
Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
ProductCategoryId INT NOT NULL FOREIGN KEY REFERENCES ProductType(Id),
Name VARCHAR(50) NOT NULL,
Description (200) NOT NULL,
Price Decimal(12,4) NOT NULL
)
然后,我们将一个新的ADO.NET实体数据模型项添加到我们的Models文件夹中。 我将其命名为ProductDataEntities
。
您的解决方案应类似于这样
请注意,Visual Studio创建了许多文件夹来组织您的项目。 我们将感兴趣的文件夹是
- Controllers
- 模型
- Views
让我们看看控制器
首先,我们需要一个Action来显示我们的页面。 在这种情况下,我们将获取一个ProductType
对象列表,并将对我们之前创建的Entity Model的引用传递给我们的.cshtm页面:
public ActionResult Index(){
var productTypes = db.ProductTypes.OrderBy(p=>p.Name);
ViewBag.ProductTypes = productTypes;
return View();
}
我们还需要一个JsonResult
将ProductCategory
数据传递回第二个DropDownList
[AllowVerbs(HtmlVerbs.Get)]
public JsonResult GetCategoryList(string ptypeid){
if (string.IsNullOrEmpty*ptypeid)
return Json(HttpNotFound());
var categoryList = GetCategoryList(Convert.ToInt32(Ptypeid));
var categoryData = categoryList.Select(m => new SelectListItem(){
Text = m.Name,
Value = m.Id.ToString()
});
return Json(categoryData, JsonRequestBehavior.AllowGet);
}
我们还需要一个小的辅助方法来获取JsonResult
中的categoryData
参数的对象列表:
private IList<ProductCategory> GetCategoryList(int ptypeid){
return db.ProductCategory.OrderBy(c=>c.Name).Where(c=>c.ProductTypeId == ptypeid).ToList();
}
最后,但并非最不重要的是一个ActionResult
,它将渲染我们的PartialView
public ActionResult GetProducts(string Id)
{
int i = Convert.ToInt32(Id)
var products = db.Products.OrderBy(p=>p.Name).Where(p=>p.ProductCategoryId == id);
return PartialView(products);
}
现在我们已经完成了C#方面的工作(是的,这确实是所需的所有代码),我们可以继续使用View
View
我们的Index
View仅包含几个DropDownList
对象和一个名为ddlproducts
的空div
元素。
@Model ProductData.Models.ProductDataEntities
...
<div> <h4> Product Types </h4>
<p>@Html.DropDownListFor(model => model.ProductTypes,
new SelectList(@ViewBag.ProductTypes, "Id", "Name"),
"--select a Product Type--",
new {
id = "ddlProductTypes"
})</p>
<h4>Product Category</h4>
<p>@Html.DropDownList("Id", new SelectList(
Enumerable.Empty<SelectListItem>(), "Id", "Name"),
"-- select a Product Category --",
new
{
id = "ddlProductCategorys",
data_url = Url.Action("GetProducts", "Home")
})</p>
</div>
<div id="ddlproducts"></div>
请注意第二个DropDownList
中的data_url = Url.Action("GetProducts", "Home")
参数。 这告诉我们的脚本在change
事件上调用什么方法。 GetProducts
Action返回一个PartialView
,该View被加载到我们空的div
元素中。
PartialView
列出我们返回的Product对象
@model IEnumerable<CascadingDDL_MVC.Models.Product>
@Html.ActionLink("Create New", "Create")
<table><tr
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
<th>
@Html.DisplayNameFor(model => model.Description)
</th>
<th>
@Html.DisplayNameFor(model => model.Price)
</th>
<th><
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Description)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
@Html.ActionLink("Details", "Details", new { id=item.Id }) |
@Html.ActionLink("Delete", "Delete", new { id=item.Id })
</td>
</tr>
</table>
现在所有这些C#和CSHTML都很好,但在目前阶段,我们的示例并没有做太多。 当然,ProductType
DropDownList将被数据填充,但是当您选择一个项目时,什么也不会发生。 这就是JavaScript Voodoo的用武之地。
JavaScript
免责声明 - 我发现JavaScript令人沮丧。 很难阅读,而且通常看起来像来自许多猴子的输出。 但是,它是一个非常有用的工具,用于操作DOM,因此我敦促您学习它。 值得庆幸的是,我们的脚本非常简单,应该相对容易理解。 也就是说,JavaScript仍然让我感到头疼!
请注意,对拼写的关注非常重要。 请帮自己一个忙,剪切并粘贴您的参数。
在Index.cshtml文件的最底部...
<script type="text/javascript"
src="../../Scripts/jquery-1.8.2.min.js"></script>
<script type="text/javascript">
$(function () {
$("#ddlProductTypes").change(function () {
var typeid = $(this).val();
// Clear the resilt div '#ddlproducts' if there is already a list displayed
$('#products').html("");
// Get the ProductCategory data and load into second DropDownList
$.getJSON("../Home/LoadCategorys", { typeid: typeid },
function (categoryData) { //
var select = $("#ddlProductCategorys");
select.empty();
select.append($('<option/>', {
value: 0,
text: "-- select a category --"
$.each(categoryData, function (index, itemData) {
select.append($('<option/>', {
value: itemData.Value,
text: itemData.Text
}));
});
});
});
$(function () { //This is fired when we select a Product Category
$('#ddlProductCategorys').change(function () {
var url = $(this).data('url');
var value = $(this).val();
$('#products').load(url, { id: value });
});
});
});
</script>
脚本会等待一个change事件
$("#ddlProductTypes").change(function () {
当我们选择ProductType
(ddlProductType
)DropDownList中的一个项目时,会触发该事件。 反过来,这会调用我们的JsonAction
方法LoadCategorys
,该方法返回要加载到第二个DropDownList(ddlProductCategorys
)中的ProductCategory
数据列表(categoryData
)。
接下来,我们使用另一个Action方法,该方法被分配给DropDownList的数据属性
data_url = Url.Action("GetProducts", "Home")
通过将结果分配给<div id="products>
元素的load
参数。
$('#products').load(url, { id: value });
这将调用Home
Controller中的GetProducts
ActionMethod,该方法返回要加载的标记。
就这么简单。