I have written a simple sample showing how to implement cascading drop down lists in ASP.Net MVC.   In this sample the user is presented with a list of countries. Once a country is selected, a new drop down list is appears showing the states in the selected country. You can download the sample here. Although there are a few simpler samples of implementing a cascading drop-down list in ASP.Net MVC, most of them mix JavaScript and markup (i.e. they are not unobtrusive), and they don’t work when deployed to IIS. Ideally you enhance functionality with JavaScript, you don’t require it. To keep this sample as simple as possible, it doesn’t implement the traditional server post back method used JavaScript is not available. Instead this sample shows best JavaScript/jQuery practices when using the jQuery.getJSON method to populate a drop-down list box. It is not meant to be a sample showing how to implement this functionality when JavaScript is not enabled. Implementing server postback to populate the ListBox when JavaScript is disabled is not difficult and not addressed in this sample.

1st

image

image

Hitting the submit button displays your selection.

image

If JavaScript is Disabled

If JavaScript is disabled in the browser …

disableJavaScript

you get a message This site requires JavaScript

image

That message is delivered with the HTML4  <noscript> element in the Views\Shared\_Layout.cshtml file.

    <noscript>
        <div>
            <h2>This site requires JavaScript</h2>
        </div>
    </noscript>

I’ve modified the Site.css file and set the main ID to not display.

#main {
    display: none;
    padding: 30px 30px 15px 30px;
 }

Browsers that have JavaScript enabled run Scripts\myReady.js, which shows the DOM element with ID main.

$(function () {
    $('#main').show();  
});

 

The Magic of jQuery

The IndexDDL action method creates a SelectList of countries, stores it in the ViewBag and passes it to the IndexDDL view.

public SelectList GetCountrySelectList() {

    var countries = Country.GetCountries();
    return new SelectList(countries.ToArray(),
                        "Code",
                        "Name");

}

public ActionResult IndexDDL() {

    ViewBag.Country = GetCountrySelectList();
    return View();
}

The IndexDDL view is shown below.

1IndexDDL

The Countries DropDownList has an ID CountriesID (shown in yellow highlight above), which enable the Scripts/countryState.js script to hook up changes in the country selection (yellow highlight below). The Scripts/countryState.js file is shown below.

$(function () {

    $('#StatesDivID').hide();
    $('#SubmitID').hide();

    $('#CountriesID').change(function () {
        var URL = $('#CountryStateFormID').data('stateListAction');
        $.getJSON(URL + '/' + $('#CountriesID').val(), function (data) {
            var items = '<option>Select a State</option>';
            $.each(data, function (i, state) {
                items += "<option value='" + state.Value + "'>" + state.Text + "</option>";
                // state.Value cannot contain ' character. We are OK because state.Value = cnt++;
            });
            $('#StatesID').html(items);
            $('#StatesDivID').show();

        });
    });

    $('#StatesID').change(function () {
        $('#SubmitID').show();
    });
});

The IndexDDL view stashes the URL to get the states associated with the selected country in the HTLM5 data dash attribute data-stateListAction . C# doesn’t allow a dash (-) in variable names, so we use underscore and Razor converts it to a dash character. The highlighted code below shows how we store and retrieve the URL used to get the states list.

@{
    ViewBag.Title = "Classic Cascading DDL";
}

@using (Html.BeginForm("IndexDDL", "Home", FormMethod.Post, 
    new { id = "CountryStateFormID", 
          data_stateListAction = @Url.Action("StateList") })) {
    <fieldset>
        <legend>Country/State</legend>
        @Html.DropDownList("Countries", ViewBag.Country as SelectList,
            "Select a Country", new { id = "CountriesID" })
        <div id="StatesDivID" >
            <label for="States">States</label>
            <select id="StatesID"  name="States"></select>
        </div>
        <p>
            <input type="submit" value="Submit" id="SubmitID" />
        </p>
    </fieldset>
}

<script src="@Url.Content("~/Scripts/countryState.js")"></script>

The Scripts/countryState.js script uses the CountryStateFormID to harvest the data-stateListAction attribute, which is the URL for the StateList action method in the home controller. The Scripts/countryState.js file is shown below.

$(function () {

    $('#StatesDivID').hide();
    $('#SubmitID').hide();

    $('#CountriesID').change(function () {
        var URL = $('#CountryStateFormID').data('stateListAction');
        $.getJSON(URL + '/' + $('#CountriesID').val(), function (data) {
            var items = '<option>Select a State</option>';
            $.each(data, function (i, state) {
                items += "<option value='" + state.Value + "'>" + state.Text + "</option>";
                // state.Value cannot contain ' character. We are OK because state.Value = cnt++;
            });
            $('#StatesID').html(items);
            $('#StatesDivID').show();

        });
    });

    $('#StatesID').change(function () {
        $('#SubmitID').show();
    });
});

The Index method is similar to the IndexDDL method, except the Index method doesn’t build  a SelectList of countries and pass it to the view via the ViewBag. The Index view is hooked up to the Scripts\GetCountry.js script to retrieve the list of countries.

For more information on working with the ASP.NET MVC DropDownList, see my sample Using the DropDownList and ListBox with ASP.NET MVC.

Rick.Anderson at Microsoft.com