views:

113

answers:

3

Hi!

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?

A: 

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.

griegs
That sounds like a good solution but there might be alot of work if the user send a form that is not valid to the service and the form returns to the client. The dropdowns must be created and selected as thay where before the call to server. Do you maby have a sample project for this procedure?
SnowJim
@SnowJim, I think maybe you have this wrong. You don't need to re-create the dropdowns that are already visible. all you are doing is an ajax call which doesn't refresh the screen. you are then returning a small piece of the whole page and putting it on the page. there is no need to re-create anything. i can provide source but you may want to look into jquery ajax calls and returning partial views first as i think this will give you that "Ah-Ha" moment.
griegs
Hi, I do understand what you are going with this. The call returns a partial view whitch is a codesnippet of HTML, this code is placed in the div. But say that I fill a form and use 4 dropdowns(1 is default), when hitting submit the entity will be sent to the server where it will be invalid(I hade not entered the correct info) when the default view is returned there will only be 1 dropdown(the default one). I will have to select the correct categorys again. Or is there a way to fix this?
SnowJim
A: 

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?

SnowJim
+2  A: 

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();
Johannes Setiabudi
Thanks so much for this. It really helped me!
RememberME