Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Cascading DropDownLists and PartialViews

0.00/5 (No votes)
13 Jun 2013 1  
Cascading DropDownLists in MVC with JavaScript Voodoo to display PartialViews.

Introduction

A short article that describes how to wire up DropDownLists in MVC Razor pages to dynamically update and also populate the page with PartialViews.

Background 

MVC Image

Being new to MVC and Web Development, I struggled to get my head around JavaScript talking to my Controller Actions. After having scoured the web and asked multiple inane questions, this is a simple solution that I have cobbled together and found useful.

If you don't know about the MVC pattern and/or Entity Framework then this may not be the article for you as I will not go in depth explaining how to wire these up.

Using the code

A couple of things are required here. I am using the Entity Framework - Model First approach, so we need to set up a simple database.

This could be done with static models but this is just as trivial. A script is provided, but the schema is simple: 

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
)

We then Add a new ADO.NET Entity Data Model Item to our Models folder. I have called this ProductDataEntities.

Your Solution should look similar to this:

Solution Image

Notice that Visual Studio created a raft of folders to organize your project. The ones we will be interested in are:

  1. Controllers 
  2. Models
  3. Views 

Let's look at the Controller:

First off we need an Action to display our page.  In this case we are going to get a list of ProductType objects and pass a reference to the Entity Model we created earlier to our .cshtm page: 

public ActionResult Index(){
 var productTypes = db.ProductTypes.OrderBy(p=>p.Name);
 ViewBag.ProductTypes = productTypes;
 return View();
}

We also require a JsonResult to pass the ProductCategory data back to the second 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); 
}  

We also need a small helper method to get a List of objects for the categoryData argument in the JsonResult

private IList<ProductCategory> GetCategoryList(int ptypeid){
  return db.ProductCategory.OrderBy(c=>c.Name).Where(c=>c.ProductTypeId == ptypeid).ToList();
}

And last but not least an ActionResult that will render our 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);
}

Now that we have completed the C# side of things (yes this really all the code that is required) we can move on to the View:

The View:

Our Index View simply contains a couple of DropDownList objects and an empty div element named ddlproducts.

@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>

Notice the data_url = Url.Action("GetProducts", "Home") argument in the second DropDownList. This tells our script what method to call on the change event. The GetProducts Action returns a PartialView that is loaded into our empty div element.

The PartialView   

List our returned Product objects:

@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>

Now all this C# and CSHTML is well and good, but at this stage, our sample doesn't do much. Sure, the ProductType DropDownList will be populated with data, but when you select an item, nothing happens. This is where the JavaScript Voodoo comes in. 

JavaScript 

Disclaimer - I find JavaScript frustrating. It is hard to read and often looks like the output from so many monkeys. It is however, a very useful tool for manipulating the DOM and therefore I urge you to learn up on it. Thankfully, our script is very straight-forward and should be relatively easy to follow.   That said, JavaScript still hurts my eyes!

Be aware that attention to spelling is VERY important. Do yourself a favor and cut and paste your arguments.

At the very bottom of the Index.cshtml file...  

<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> 

The script waits until a change event 

$("#ddlProductTypes").change(function () {

which is fired  when we select an item in the ProductType (ddlProductType) DropDownList. This in turn calls our JsonAction method, LoadCategorys which returns the list of ProductCategory data (categoryData) to be loaded into the second DropDownList (ddlProductCategorys).

Next we use another Action method which we assigned to the DropDownList's data  attribute 

data_url = Url.Action("GetProducts", "Home") 

by assigning the result to the <div id="products> element's load arguments. 

$('#products').load(url, { id: value });  

This calls the GetProducts ActionMethod in the Home Controller which returns the markup to be loaded.

Simples.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here