Introduction
There are several ways you can populate data for a dependent Dropdown
list. Let me explain what I mean by dependent Dropdown
list. For example, in your application, user need to select a Country
. Based on user selection, you need to show only the State
/Province
of the selected country
. Again, when user selects a State
/Province
, you need to show only the Cities of the selected State
/Province
. Here, State
/Province
and City
dropdown list are dependent dropdown
lists.
One option is, you can load all of the countries, state
/province
and cities in page (memory) and filter them using JQuery/JavaScript. You may need to use very complex query to retrieve data for dependent dropdown list. Or you may have to retrieve huge amount of data. For those type of scenarios, loading all data in memory is not a good solution.
Using MVC’s Action and JQuery’s Load
methods, you can retrieve data for dependent dropdown
list (State
/Province
) as user selects the parent (Country
). In this walk through, you will learn how to use the Action
and Load
methods to re-load a part of your page asynchronously.
Please note that I am concentrating on the functionality instead of making it nice. In this demonstration, I am going to use Country
, StateProvince
and City
objects.
1.0 Create MVC 4 Web Application
Using Visual Studio 2012, create a new project with MVC 4 Web Application. Select the template either Internet or Intranet application. I have selected the Internet Application Template. Make sure Razor is selected in the View Engine. Name the project “DependentDropDownList
”.
By default, Visual Studio creates several files for Controllers, Models and Views. In this tutorial, you are going to modify the landing page of the application. In other words, you are going to use the Index.cshtml view to display data in different dropdown lists.
As a first step for cleanup, open the Index.cshtml and delete all lines of code and save the file.
2.0 Add JQuery Scripts to Project
Open the _Layout.cshtml from the Views->Shared folder and add the following JQuery references just before the end tag (</head>
) of Head
.
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script src="~/Scripts/jquery-ui-1.8.24.min.js"></script>
3.0 Create Models
Each dropdown list has item with text and value. Text is used to display data in the screen. For binding data to dropdown list, we need to define an IEnumerable<SelectedListItem>
variable, which will have item text and value. Again, to capture user selection, we need to define another variable (e.g., Country ID
). To define them together, we will need to have a separate class for each dropdown list.
Hence, for this walk through, you will need to define two sets of models – one set for defining hierarchal data and another set for controlling dropdown
list.
To keep the two sets separate, create two folders under the Models. From the Solution Explorer, right click on Models and select New Folder. Type the name DataModel. Similarly, create another folder and name it ViewModel.
3.1 Create Data Models
From the Solution Explorer, right click on the DataModel folder and select Add option from context menu. Then select Class option and name it CityDataModel
. Now, the Models folder should have the CityDataModel.cs class. Similarly, create StateProvinceDataModel
and CountryDataModel
models.
Open CityDataModel.cs and add fields as follows. You may add validation and others attribute. To concentrate only on the dynamic loading, keep it simple.
namespace DependentDropDownList.Models.Data
{
public class CityDataModel
{
public Int32 ID { get; set; }
public String CityName { get; set; }
}
}
Open StateProvinceDataModel.cs and add fields as follows. This class contains a list of CityDataModel
class.
namespace DependentDropDownList.Models.Data
{
public class StateProvinceDataModel
{
public Int32 ID { get; set; }
public String StateOrProvinceName { get; set; }
public List<CityDataModel> CityList = new List<CityDataModel>();
}
}
Open CountryData.cs and add fields as follows. This class contains a list of StateProvinceData
class.
namespace DependentDropDownList.Models.Data
{
public class CountryDataModel
{
public Int32 ID { get; set; }
public String CountryName{ get; set; }
public List<StateOrProvinceDataModel>
StateOrProvinceList= new List<StateOrProvinceDataModel>();
}
}
3.2 Create View Models
From the Solution Explorer, right click on the ViewModel folder and select Add option from context menu. Then select Class option and name it CityViewModel
. Now, the Models folder should have the CityViewModel.cs class. Similarly, create StateProvinceViewModel
and CountryViewModel
models.
Open CityViewModel.cs and add the following lines of code. CityList
contains the list of cities based on the selection of country and state/province. SelectedCityID
keeps the user selection of city and CityIEnum
converts the List of Cities to SelectList
, which is needed by dropdown
list.
namespace DependentDropDownList.Models.ViewModel
{
public class CityViewModel
{
public List<Models.DataModel.CityDataModel> CityList =
new List<Models.DataModel.CityDataModel>();
public Int32 SelectedCityID {get; set;}
public IEnumerable<SelectListItem> CityIEnum
{
get
{
return new SelectList(CityList, "ID", "CityName");
}
}
}
}
Open StateProvinceViewModel.cs and add fields as follows. StateProvinceList
contains the list of state/provinces based on the selection of country. SelectedStateProvinceID
keeps the user selection of state/province and StateProvinceIEnum
converts the list of state/province to SelectList
, which is needed by drop down list.
namespace DependentDropDownList.Models.ViewModel
{
public class StateProvinceViewModel
{
public List<Models.DataModel.StateProvinceDataModel> StateProvinceList=
new List<Models.DataModel.StateProvinceDataModel>();
public Int32 SelectedStateProvinceID {get; set;}
public IEnumerable<SelectListItem> StateProvinceIEnum
{
get
{
return new SelectList(StateProvinceList, "ID", "StateOrProvinceName");
}
}
}
}
Open CountryViewModel.cs and add fields as follows. CountryList
contains the list of countries. SelectedCountryID
keeps the user selection of country and CountryIEnum
converts the list of country to SelectList
, which is need by dropdown
list.
namespace DependentDropDownList.Models.ViewModel
{
public class CountryViewModel
{
public List<Models.DataModel.CountryDataModel> CountryList=
new List<Models.DataModel.CountryDataModel>();
public Int32 SelectedCountryID {get; set;}
public IEnumerable<SelectListItem> CountryIEnum
{
get
{
return new SelectList(CountryList, "ID", "CountryName");
}
}
}
}
4.0 Modify Controller (HomeController)
For this demo application, as you are going to use the landing page, modify HomeController
. In real life application, you may need to display the data in different view. In that case, you will need to add/modify the corresponding controller.
4.1 Define Country, State/Province and City data
For simplicity, use static
data instead of using backend data storage. Add a static
list of CountryData
object in the HomeController
. CountryData
will contains StateProvinceData
and each StateProvinceData
will contain CityData
.
public static List<Models.DataModel.CountryDataModel> Countries =
new List<Models.DataModel.CountryDataModel>()
{
new Models.DataModel.CountryDataModel
{ ID = 1, CountryName = "USA",
StateOrProvinceList =
new List<Models.DataModel.StateProvinceDataModel>()
{
new Models.DataModel.StateProvinceDataModel
{
ID = 1, StateOrProvinceName = "Michigan",
CityList = new List<Models.DataModel.CityDataModel>()
{
new Models.DataModel.CityDataModel {ID = 1,
CityName = "Detroit"},
new Models.DataModel.CityDataModel {ID = 2,
CityName = "Saginaw"},
new Models.DataModel.CityDataModel {ID = 3,
CityName = "Troy"}
}
},
new Models.DataModel.StateProvinceDataModel
{
ID = 2, StateOrProvinceName = "New York",
CityList = new List<Models.DataModel.CityDataModel>()
{
new Models.DataModel.CityDataModel {ID = 1,
CityName = "Manhatan"},
new Models.DataModel.CityDataModel {ID = 2,
CityName = "Woodside"},
new Models.DataModel.CityDataModel {ID = 3,
CityName = "Jackson Height"}
}
},
new Models.DataModel.StateProvinceDataModel
{
ID = 3, StateOrProvinceName = "Illinois",
CityList = new List<Models.DataModel.CityDataModel>()
{
new Models.DataModel.CityDataModel {ID = 1,
CityName = "Chicago"},
new Models.DataModel.CityDataModel {ID = 2,
CityName = "Aurora"},
new Models.DataModel.CityDataModel {ID = 3,
CityName = "Benton"}
}
}
}
},
new Models.DataModel.CountryDataModel
{ ID = 2, CountryName = "Canada",
StateOrProvinceList =
new List<Models.DataModel.StateProvinceDataModel>()
{
new Models.DataModel.StateProvinceDataModel
{
ID = 1, StateOrProvinceName = "Ontario",
CityList = new List<Models.DataModel.CityDataModel>()
{
new Models.DataModel.CityDataModel {ID = 1,
CityName = "Windsor"},
new Models.DataModel.CityDataModel {ID = 2,
CityName = "Toronto"},
new Models.DataModel.CityDataModel {ID = 3,
CityName = "Oshawa"}
}
},
new Models.DataModel.StateProvinceDataModel
{
ID = 2, StateOrProvinceName = "Quibec",
CityList = new List<Models.DataModel.CityDataModel>()
{
new Models.DataModel.CityDataModel {ID = 1,
CityName = "Montreal"},
new Models.DataModel.CityDataModel {ID = 2,
CityName = "Quibec City"}
}
}
}
}
};
4.2 Action Methods
Country
, State
/Province
and City dropdown
list will have their own view. Main landing page view will load them dynamically. Hence, Index
action method will return the view without passing any object.
public ActionResult Index()
{
return View();
}
To populate country data, define an action method name CountryView
. This method will populate data from the list of CountryDataModel
into CountryViewModel
and return the data to CountryView
. Code for the action method is as follows:
public static Models.ViewModel.CountryViewModel cvm =
new Models.ViewModel.CountryViewModel();
public ActionResult CountryView()
{
cvm.CountryList.Clear();
foreach (Models.DataModel.CountryDataModel cd in Countries)
{
cvm.CountryList.Add(cd);
}
return View(cvm);
}
To populate state/province data, define an action method named StateProvinceView
with a parameter for selected country id. This method will populate data from the list of StateProvinceDataModel
into StateProvinceViewModel
based on the selected country. Code for the action method is as follows:
public static Models.ViewModel.StateProvinceViewModel spvm =
new Models.ViewModel.StateProvinceViewModel();
public ActionResult StateProvinceView(int? countryID)
{
spvm.StateProvinceList.Clear();
if (countryID != null)
{
Models.DataModel.CountryDataModel cd =
Countries.Find(p => p.ID == countryID);
foreach (Models.DataModel.StateProvinceDataModel spd
in cd.StateOrProvinceList)
{
spvm.StateProvinceList.Add(spd);
}
}
return View(spvm);
}
To populate city data, define another action method name CityView
with selected country and state/province id as parameters. This method will populate data from the list of CityDataModel
into CityViewModel
based on the selected country
and state
/province
ids. Code for the action method is as follows:
public static Models.ViewModel.CityViewModel cityvm =
new Models.ViewModel.CityViewModel();
public ActionResult CityView(int? countryID, int? stateprovinceID)
{
cityvm.CityList.Clear();
if (countryID != null && stateprovinceID != null)
{
Models.DataModel.CountryDataModel cd =
Countries.Find(p => p.ID == countryID);
Models.DataModel.StateProvinceDataModel spd =
cd.StateOrProvinceList.Find(p => p.ID == stateprovinceID);
foreach (Models.DataModel.CityDataModel cpd in spd.CityList)
{
cityvm.CityList.Add(cpd);
}
}
return View(cityvm);
}
5.0 Add and Update Views
For this demo, you will need to create three different views – CountryView
, StateProvinceView
and CityView
. Each view will use its own view model (e.g., CountryViewModel
, etc.). Each view will have its own JQuery functions to capture the change event of the dropdown
list and also populate corresponding data.
Index
view will invokes the child action methods defined in the HomeController
and generate the HTML from the views.
5.1 Create CountryView
From solution explorer, right click on Views->Home folder. From the context menu, select Add option and then select View. Enter the view name CountryView
. This view does not need the layout or master page. So, clear the check mark for “Use a layout or master page”. Make sure that Razor is selected in the View Engine. Click on the Add button to create the view.
Open the CountryView.cshtml and remove all Visual Studio’s generated code. First, you need to add the CountryViewModel
in the view.
@model MVCControls.Models.CountryViewModel
Next, you need to mention that it does not need layout by entering the Layout = null;
command.
@{
Layout = null;
}
Now you need to define the Dropdown
control and bind the model data.
@Html.DropDownListFor(p => p.SelectedCountryID,
Model.CountryIEnum,
"Select a Country",
new { @id="Country-DropdownID",
@class="Country-DropdownCls"})
First parameter identifies if model already has selected country
code. If so, then that value will be selected in the dropdown
list. Second parameter defines the list of countries. Third parameter data will be showed in the dropdown
list when there is no selected country. Fourth parameter creates an object to specify the attributes for generated HTML code. Those attributes will be used in JQuery functions to manipulate the data.
Next, you need to add the JQuery functions to capture the change event of country
drop down list and load data for state/province drop down list. Please, note that “StateProvinceContainer
” and “CityContainer
” are two div
tags and will be defined in the Index.cshtml.
<script type="text/javascript">
$(function () {
$('#Country-DropdownID').change(function () {
var selectedCountryID = this.value;
$('#StateProvinceContainer').load
(<a href="mailto:'@Url.Action("StateProvinceView")?countryID=
'">'@Url.Action("StateProvinceView")?countryID=' +
selectedCountryID);
$('#CityContainer').load(<a href="mailto:
'@Url.Action("CityView")'">
'@Url.Action("CityView")'</a>);
});
});
</script>
5.2 Create StateProvinceView
From solution explorer, right click on Views->Home folder. From the context menu, select Add option and then select View. Enter the view name StateProvinceView
. This view does not need the layout or master page. Clear the check mark for “Use a layout or master page”. Make sure that Razor is selected in the View Engine. Click on the Add button to create the view.
Open the StateProvinceView.cshtml and remove all Visual Studio’s generated code. First, you need to add the StateProvinceViewModel
in the view.
@model MVCControls.Models.StateProvinceViewModel
Next, you need to mention that it does not need layout by entering the Layout = null;
command.
@{
Layout = null;
}
Now you need to define the Dropdown
control and bind the model data.
@Html.DropDownListFor(p => p.SelectedStateID,
Model.StateProvinceIEnum,
"Select a State or Province",
new { @id="StateProvince-DropDownID",
@class="StateProvince-DropDownCls"})
Description of parameters were defined in 5.1 section.
Next, you need to add the JQuery functions to capture the change event of state/province drop down list and load data for city drop down list. Please, note that “CityContainer
” is a div
tag and will be defined in the Index.cshtml.
<script type="text/javascript">
$(function () {
$('#StateProvince-DropDownID').change(function () {
var selectedCountryID = $('#Country-DropdownID option:selected').val();
var selectedStateProvinceID = this.value;
$('#CityContainer').load(<a href="mailto:'@Url.Action
("CityView")?countryID='">'@Url.Action
("CityView")?countryID=' +
selectedCountryID +'&stateprovinceID=' +
selectedStateProvinceID);
});
});
</script>
5.3 Create CityView
From solution explorer, right click on Views->Home folder. From the context menu, select Add option and then select View. Enter the view name CityView
. This view does not need the layout or master page. Clear the check mark for “Use a layout or master page”. Make sure, that Razor
is selected in the View Engine. Click on the Add button to create the view.
Open the cityView.cshtml and remove all Visual Studio’s generated code. First, you need to add the CityViewModel
in the view.
@model DependentDropDownList.Models.ViewModel.CityViewModel
Next, you need to mention that it does not need layout by entering the Layout = null;
command.
@{
Layout = null;
}
Now, you need to define the Dropdown
control and bind the model data.
@Html.DropDownListFor(p=>p.SelectedCityID ,
Model.CityIEnum,
"Select a City",
New <a href="mailto:
{@id="City-DropdownID">{@id="City-DropdownID",
@class="City-DropdownCls"})
Description of parameters were defined in 5.1 section.
There is no other dropdown list or control dependent on City dropdown list. Hence, City dropdown list does need any JQuery.
5.4 Update Index View
From the solution explorer, open Index.cshtml file. Add HTML code to define a table with three columns. In the first row, specify the headers for Country
, State
/Province
and City
.
In the 2nd row 1st column, define a div
with “CountryContainer
” as an ID. Inside the div
, define Action
method of HTML helper class with “CountryView
” as a parameter.
Similarly, add method for “StateProvinceView
” and “CityView
”. Full code is as follows:
<table>
<tr>
<th>Country</th>
<th>State/Province</th>
<th>City</th>
</tr>
<tr>
<td>
<div id="CountryContainer">
@Html.Action("CountryView")
</div>
</td>
<td>
<div id="StateProvinceContainer">
@Html.Action("StateProvinceView")
</div>
</td>
<td>
<div id="CityContainer">
@Html.Action("CityView")
</div>
</td>
</tr>
</table>
6.0 Conclusion
Using Action
method of HTML helper class allows to invoke the corresponding controller action method. Load
method of JQuery object allows to re-load the part (div
) of the page dynamically.