Skip to content

CascadingDropDown with multiple dependencies (V2 only)

Alex Artyomov edited this page May 12, 2017 · 4 revisions

Use-Case Description

One of the common feature request was an ability to reload dropdown data based on multiple dependencies Consider the following example - to choose a car model based on make and a year which could look like that:

The model for this feature could look like that:

public class ChooseCarModel
{
    public IList<SelectListItem> Years { get; set; }
    public IList<SelectListItem> Makes { get; set; }
    public string SelectedYear { get; set; }
    public string SelectedMake { get; set; }
    public int? SelectedModel { get; set; }
}

The implementation:

<script>

   function beforeSendData(data) {
       return {
           make: $("#SelectedMake").val(),
           year: $("#SelectedYear").val()
       }
   }

    $(function () {
    //all the parent dropdowns are marked with parentDd class
    $(".parentDd").change(function () {
        //If all the values are set and valid, set hidden input value
        if ($("#SelectedMake").val() && $("#SelectedYear").val()) {
            $("#makeYearHidden").val(1);
        }
        else {
            $("#makeYearHidden").val(null);
        }
        //Notify cascading dropdown about changes.
        $("#makeYearHidden").trigger("change");           
    })
})

</script>

@using (Html.BeginForm("ChooseCar", "Home", FormMethod.Post))
{
  <div class="form-horizontal">
     <div class="form-group">
        <label>MAKE</label>
        @Html.DropDownListFor(m => m.SelectedMake, Model.Makes, "Please select a Make", new { @class = "form-control parentDd" })
    </div>
    <div class="form-group">
        <label>YEAR</label>
        @Html.DropDownListFor(m => m.SelectedYear, Model.Years, "Please select a Year", new { @class = "form-control parentDd" })
    </div>
    <input id="makeYearHidden" type="hidden" />

    <div class="form-group">
        <label>MODEL</label>
        @Html.CascadingDropDownListFor(expression: m => m.SelectedModel,
                            triggeredByPropertyWithId: "makeYearHidden",
                            url: Url.Action("GetModel", "Home"),
                            ajaxActionParamName: "makeYear",
                            disabledWhenParentNotSelected: true,
                            htmlAttributes: new { @class = "form-control" },
                            options: new CascadeDropDownOptions
                            {
                                BeforeSend = "beforeSendData"
                            })
    </div>       
  </div>

 <input type="submit" value="Submit" class="btn btn-primary" />

}

Controller:

    public ActionResult GetModel(string make, string year)
    {
        ...
        return  Json(result, JsonRequestBehavior.AllowGet);
    }

Explanation:

  • In order to get this working we need to create a hidden input (#makeYearHidden) that will trigger the changes in cascading dropdown. Its value should be updated each time that one of the parent dropdowns(#SelectedMake and #SelectedYear) changes and it should be set to something other than null only when all parent dropdowns are set. In order to reload cascading dropdown, hidden input should trigger a change event

  • For sending multiple values to the server we need to set CascadeDropDownOptions.BeforeSend callback to the function that will be used to modify the data before it is sent. This is exactly what beforeSendData function does in our case:

    function beforeSendData(data) {
      return {
          make: $("#SelectedMake").val(),
          year: $("#SelectedYear").val()
      }
    }
    

    The function replaces the data that is sent to server with values of SelectedMake and SelectedYear parent dropdowns.

  • Since hidden input is the one that triggers cascading dropdown, CascadingDropDownListFor.triggeredByPropertyWithId value should be set to the hidden input id (makeYearHidden in the example)

Clone this wiki locally