级联下拉列表和部分视图
使用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,该方法返回要加载的标记。
就这么简单。
