Cascading dropdowns

2019-04-16 22:28发布

问题:

I am working with cascading dropdowns in MVC. It appears that I will not be able to easily create dropdowns on demand, instead I will have to add the dropdowns before sending it to the client.

This is how I am doing it right now:

In the aspx page

                <%: Html.DropDownListFor(model => model.ModelViewAd.Category1, Model.ModelViewAd.Category1List, "-- Välj kategori --")%>
            <%: Html.DropDownListFor(model => model.ModelViewAd.Category2, Model.ModelViewAd.Category2List, "-- Välj kategori --")%>
            <%: Html.DropDownListFor(model => model.ModelViewAd.Category3, Model.ModelViewAd.Category3List, "-- Välj kategori --")%>
            <%: Html.DropDownListFor(model => model.ModelViewAd.Category4, Model.ModelViewAd.Category4List, "-- Välj kategori --")%>

This is rendered like this :

<select id="ModelViewAd_Category1" name="ModelViewAd.Category1">
    <option value="">-- V&#228;lj kategori --</option>    
    <option value="10">Fordon</option>
    <option value="15">F&#246;r hemmet</option>
    <option value="17">Bostad</option>
    </select>
<select id="ModelViewAd_Category2" name="ModelViewAd.Category2">
    <option value="">-- V&#228;lj kategori --</option>
</select>
<select id="ModelViewAd_Category3" name="ModelViewAd.Category3">
    <option value="">-- V&#228;lj kategori --</option>
</select>
<select id="ModelViewAd_Category4" name="ModelViewAd.Category4">
    <option value="">-- V&#228;lj kategori --</option>
</select>

This is what the script on the page looks like:

<script type="text/javascript">


            $(function () {
                $("select#ModelViewAd_Category1").change(function () {
                    var id = $(this).val();
                    var urlAction = "/AdCategory/GetCategoriesByParent1/" + id;
                    $.getJSON(urlAction, { id: id }, function (data) {
                        $("#ModelViewAd_Category2").addItems(data.d);
                    });
                });

                $("select#ModelViewAd_Category2").change(function () {
                    var id = $(this).val();
                    var urlAction = "/AdCategory/GetCategoriesByParent1/" + id;
                    $.getJSON(urlAction, { id: id }, function (data) {
                        $("#ModelViewAd_Category3").addItems(data.d);
                    });
                });

                $("select#ModelViewAd_Category3").change(function () {
                    var id = $(this).val();
                    var urlAction = "/AdCategory/GetCategoriesByParent1/" + id;
                    $.getJSON(urlAction, { id: id }, function (data) {
                        $("#ModelViewAd_Category4").addItems(data.d);
                    });
                });



            });


    </script>

And then I have an included file that contains this:

$.fn.clearSelect = function () {
    return this.each(function () {
        if (this.tagName == 'SELECT')
            this.options.length = 0;
    });
}

$.fn.addItems = function (data) {
    return this.clearSelect().each(function () {
        if (this.tagName == 'SELECT') {
            var dropdownList = this;
            $.each(data, function (index, optionData) {
                var option = new Option(optionData.Text,
                         optionData.Value);
                if ($.browser.msie) {
                    dropdownList.add(option);
                }
                else {
                    dropdownList.add(option, null);
                }

                if ($(this).children().size() < 2) {
                    $(this).hide();
                }
                else {
                    $(this).show();
                }
            });
        }
    });
}

The problem I now have is that I need to hide the dropdowns that do not contain any options or only contain one option. This should be checked when doing a call to the service as well as when the page is sent to the client ("POSTBACK").

What I need is :

  • 4 Dropdowns
  • Only the first dropdown is visible when first entering the page.
  • When selecting an option from dropdown1 the dropdown2 will be populated and so on
  • If there is only 1 option then the dropdown should be hidden
  • If all 4 dropdowns are set and the end-user changes dropdown1, then dropdown2 should be reloaded and the rest be hidden
  • If the user has selected some of the dropdowns (say 1, 2 and 3) and hit submit and the page is not accepted on the server side (not valid) the dropdowns should be set exactly as when the user clicked the submit button when the page returns to the user.

Any suggestions on this?

回答1:

I wrote a blog post about this here (minus the hiding part - which you seem to have nailed down). Essentially, you will need to rebuild the dropdown with the selected values if there is an error.

$("#ClientId").change(function () {
    var clientId = "";
    $("#ClientId option:selected").each(function () {
        clientId += $(this)[0].value;
    });
    var url = '<%:Url.Action("ProjectList", "Client") %>' + "/" + clientId;
    $.getJSON(url, null, function (data) {
        var selectedValue = '<%:Model.ProjectId %>';
        $("#ProjectId").empty();
        $.each(data, function (index, optionData) {
            if (optionData.OBJID == parseInt(selectedValue))
                $("#ProjectId").append("<option value='" + optionData.ObjId+ "' selected='true'>" + optionData.Name + "</option>");
            else 
                $("#ProjectId").append("<option value='" + optionData.ObjId + "'>" + optionData.Name + "</option>");
        });
    });
}).change();


回答2:

There is another way to do this using PartialViews.

If you create a partial view for each dropdown, or a generic one if you can, then you simply render the first one on load.

When you change the selection of a dropdown you do an ajax postback which does one of two things.

1) it checks to see if you have a selection in the dropdown. If you do thet\n it grabs data and does a return PartialView("PVName", PVModel); This will return your partial view as fully rendered html.

2) if there is no selection then return an empty string or null;

Your jquery then simply replaces a div with the contents of the returned html. in the case of null you'd replace the dic with nothing which effectively removes the dropdown.

So you html might look like this;

<div class="firstdd"><dropdown/></div>

<div class="seconddd"></div>

on callback from the ajax call you simply $('.seconddd').html(returnedHTML);

i hope this makes sense and simplifies your code somewhat.



回答3:

I did not find any solution on the prevouse solution so I decided to try this : http://weblogs.asp.net/rajbk/archive/2010/05/20/cascadingdropdown-jquery-plugin-for-asp-net-mvc.aspx

The problem here is that I need to hide the dropdowns that are not selected and to acomplish this I have changed to this :

post: function () {
                        methods.showLoading();
                        $.isFunction(config.onLoading) && config.onLoading.call($this);
                        $.ajax({
                            url: actionPath,
                            type: 'POST',
                            dataType: 'json',
                            data: ((typeof config.postData == "function") ? config.postData() : config.postData) || 'id=' + $(source).val(),
                            success: function (data) {
                                methods.reset();
                                $.each(data, function () {
                                    $this.append($(optionTag)
                                        .attr("value", this.Value)
                                        .text(this.Text));
                                });

                                if (hideEmpty == true) {

                                    if ($this.children().size() < 2) {
                                        $this.css("visibility", "hidden");
                                    }
                                    else {
                                        $this.css("visibility", "visible");
                                    }
                                }

                                methods.loaded();
                                $.isFunction(config.onLoaded) && config.onLoaded.call($this);
                            },
                            error: function () {
                                methods.showError();
                            }
                        });
                    }
                }; 

As you can see, all I have added is the

                        if (hideEmpty == true) {

                            if ($this.children().size() < 2) {
                                $this.css("visibility", "hidden");
                            }
                            else {
                                $this.css("visibility", "visible");
                            }
                        }

The effect of this is that it will work grate untill the user posts a form that is not correct and the view is returned to the client. Then all but the first dropdown is hidden even when the other dropdowns is set?

I supose I will have to run some function when MVC sets the dropdown to check if it should be hidden or not, but how do I do this?