Javascript – MVC 4 Razor – Creating a dynamic DropDownList

asp.net-mvccjavascriptnetrazor

I'm trying to create a view which has two DropDownLists. The options available in the second DropDownList depend upon what the user has selected in the first. I'm passing this data to my view in the ViewBag as follows:

   List<SelectListItem> firstBoxChoices =  ViewBag.firstBoxChoices;
   Dictionary<string, List<SelectListItem>> secondBoxDict = ViewBag.secondBoxDict;

The first object has the choices for the first DropDownList. When the user selects one of those, I need to get the appropriate list of choices for the second DropDownList from my Dictionary. I just can't figure out how to achieve this. If I get the new selection of the first DropDownList in a Javascript onchange() function, there doesn't seem to be any way to use this value as the key for my C# dictionary.

Of course, I've seen this functionality on the web so I know it must be possible somehow. How can I achieve this?

Thanks!

Best Answer

There are a couple ways of doing this without forcing you to store all the possible data items in the model, my preference is to use Javascript/JQuery. Here is an example of a Country/State cascading drop down:

Javascript used to get states when a country is selected:

<script type="text/javascript">
    function AppendUrlParamTokens(url, params) {

        for (var param in params) {
            if (params[param] == null) {
                delete params[param];
            }
        }

        return url + "?" + jQuery.param(params);
    }

    function OnCountriesChange(ddl) {
        jQuery.getJSON(AppendUrlParamTokens('@Url.Action("GetStates", "Data")', { countryId: ddl.options[ddl.selectedIndex].value }), function (result) {
            var target = jQuery('#states_ddl');
            target.empty();
            jQuery(result).each(function() {
                jQuery(document.createElement('option'))
                    .attr('value', this.Value)
                    .text(this.Text)
                    .appendTo(target);
            });
        });
    };
</script>

Country dropdown:

@Html.DropDownListFor(model => model.Country, new SelectList(Model.Countries, "Value", "Text", Model.PreviousCountrySelected), "(Select One)", new { id = "countries_ddl", onchange = "OnCountriesChange(this)" })

State dropdown:

Html.DropDownListFor(model => model.State,
                              Model.States != null
                                       ? new SelectList(Model.States, "Value", "Text", Model.PreviousStateSelected)
                                       : new SelectList(new List<SelectListItem>(), "Value", "Text"),
                              new { id = "states_ddl" })

Controller method to retrieve states:

public ActionResult GetStates(short? countryId)
{
    if (!countryId.HasValue)
    {
        return Json(new List<object>(), JsonRequestBehavior.AllowGet);
    }

    var data = GetAllStatesForCountry(countryId.Value).Select(o => new { Text = o.StateName, Value = o.StateId });

    return Json(data, JsonRequestBehavior.AllowGet);
}

The idea is that on selection of dropdown 1 you use ajax to go retrieve your second dropdown's value.

Edit: Forgot to include utility method for constructing urls