Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Dynamically Populate Dependent Dropdown List in MVC

4.68/5 (14 votes)
7 Dec 2014CPOL9 min read 165.9K   5.1K  
Using MVC’s Action and JQuery’s Load methods, you can retrieve data for dependent dropdown list as user selects the parent.

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.

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

C#
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.

C#
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.

C#
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.

C#
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.

C#
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.

C#
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.

C#
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.

C#
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:

C#
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:

C#
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:

C#
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.

C#
@model MVCControls.Models.CountryViewModel

Next, you need to mention that it does not need layout by entering the Layout = null; command.

C#
@{
    Layout = null;
}

Now you need to define the Dropdown control and bind the model data.

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

HTML
<script type="text/javascript">
    $(function () {
        // Populate State/Provinces
        $('#Country-DropdownID').change(function () {
                var selectedCountryID = this.value;

        $('#StateProvinceContainer').load
        (<a href="mailto:'@Url.Action("StateProvinceView")?countryID=
        '">'@Url.Action("StateProvinceView")?countryID=' +
                                      selectedCountryID);
        // clear up the city drop down list as no state is selected
                $('#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.

HTML
@model MVCControls.Models.StateProvinceViewModel

Next, you need to mention that it does not need layout by entering the Layout = null; command.

C#
@{
    Layout = null;
}

Now you need to define the Dropdown control and bind the model data.

C#
@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.

HTML
<script type="text/javascript">
    $(function () {
        // Populate City
        $('#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.

C#
@model DependentDropDownList.Models.ViewModel.CityViewModel

Next, you need to mention that it does not need layout by entering the Layout = null; command.

HTML
@{
    Layout = null;
}

Now, you need to define the Dropdown control and bind the model data.

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

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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)