使用MVC2基于另一个下拉列表填充下拉列表(级联下拉列表)

6
我正在制作一个与车辆相关的应用程序。我需要两个下拉列表:
- 制造商:所有车辆制造商 - 型号:属于所选制造商DropDownList的型号
在MVC2中如何实现这一点?
我的想法是:当选择第一个列表时,我使用ajax调用,然后将Models返回绑定到Model DDL?这样如何使用模型绑定?
更新:我发布了我最终做的答案。它非常简单且效果很好。
如果你愿意,也可以使用get,但必须指定你想要像这样... return Json(citiesList, JsonRequestBehavior.AllowGet);
3个回答

13

这就是我的解决方案......不需要额外的插件/ 1000行代码......

HTML

//The first DDL is being fed from a List in my ViewModel, You can change this...
<%: Html.DropDownList("MakeList", new SelectList(Model.Makes, "ID", "Name")) %>
<select id="ModelID" name="ModelID" disabled="disabled"></select>

JQuery

    $(document).ready(function () {
        $('#MakeList').change(function () {
            $.ajaxSetup({ cache: false });
            var selectedItem = $(this).val();
            if (selectedItem == "" || selectedItem == 0) {
                //Do nothing or hide...?
            } else {
                $.post('<%: ResolveUrl("~/Sell/GetModelsByMake/")%>' + $("#MakeList > option:selected").attr("value"), function (data) {
                    var items = "";
                    $.each(data, function (i, data) {
                        items += "<option value='" + data.ID + "'>" + data.Name + "</option>";
                    });
                    $("#ModelID").html(items);
                    $("#ModelID").removeAttr('disabled');
                });
            }
        });
    });

行动

    [HttpPost]
    public ActionResult GetModelsByMake(int id)
    {
        Models.TheDataContext db = new Models.TheDataContext();
        List<Models.Model> models = db.Models.Where(p=>p.MakeID == id).ToList();

        return Json(models);
    }

最好将Ajax调用和填充选择器的逻辑移动到一个单独文件中的新方法中,这样它更具可重用性。 - Manaf Abu.Rous
没错 - 我可能会这样做。在让它正常运行之前,我不想让它变得过于可扩展... ;) - Jason
我尝试了你的解决方案,但是我无法使其工作。我做了和你一样的事情。我首先正确填充了DDL。当我在Firebug中调试它时,我发现它在以下行处中断: $.post('<%: ResolveUrl("~/Sell/GetModelsByMake/")%>' + $("#MakeList > option:selected").attr("value"), function (data) { 我有正确的操作,并且它应该接受 $(“#MakeList > option:selected”).attr(“value”),但它从未调用该操作。我做错了什么?我错过了什么吗?非常感谢任何建议。 附言:我正在使用: jquery-1.5.1.min.js和jquery-ui-1.8.11.custom.min.js - Slavisa
不看生成的内容,我的猜测是要么$.post解析URL时出现错误,要么您从DDL值中传递了一个字符串,例如'honda',而您的控制器期望一个int类型的ID('1')。无论哪种情况,它都无法触发您的操作。 - Jason

9
这里有一个不错的方法:
假设我们有两个下拉列表,国家和城市,城市下拉列表默认为禁用状态,当选择一个国家时会发生以下情况:
1. city drop down list gets enabled.
2. An AJAX call is made to an action method with the selected country and a list of cities is returned.
3. the city drop down list is populated with the JSON data sent back.

原始代码的荣誉归于King Wilder,来源于MVC Central。这个例子是从他在Golf Tracker Series中的代码中提取出来的简化版本。
HTML
<select id="Country">
// a List of Countries Options Goes Here.
</select></div>

<select id="City" name="City" disabled="disabled">
// To be populated by an ajax call
</select>

JavaScript

// Change event handler to the first drop down ( Country List )
$("#Country").change(function() {
    var countryVal = $(this).val();
    var citySet = $("#City");

    // Country need to be selected for City to be enabled and populated.
    if (countryVal.length > 0) {
        citySet.attr("disabled", false);
        adjustCityDropDown();
    } else {
        citySet.attr("disabled", true);
        citySet.emptySelect();
    }
});

// Method used to populate the second drop down ( City List )   
function adjustCityDropDown() {
    var countryVal = $("#Country").val();
    var citySet = $("#City");
    if (countryVal.length > 0) {
        // 1. Retrieve Cities that are in country ...
        // 2. OnSelect - enable city drop down list and retrieve data
        $.getJSON("/City/GetCities/" + countryVal ,
        function(data) {
            // loadSelect - see Note 2 bellow
            citySet.loadSelect(data);
        });
    }
}

动作方法

[HttpGet]
public ActionResult GetCities(string country)
{
    Check.Require(!string.IsNullOrEmpty(country), "State is missing");

    var query  = // get the cities for the selected country.

    // Convert the results to a list of JsonSelectObjects to 
    // be used easily later in the loadSelect Javascript method.         
    List<JsonSelectObject> citiesList = new List<JsonSelectObject>();
        foreach (var item in query)
        {
            citiesList.Add(new JsonSelectObject { value = item.ID.ToString(),
                                                  caption = item.CityName });
        }        

    return Json(citiesList, JsonRequestBehavior.AllowGet);
}

重要提示:

1. JsonSelectObject 在将结果转换为选项标签并在下面的javascript loadSelect方法中使用时,有助于使事情变得更加容易。它基本上是一个具有两个属性值和标题的类:

public class JsonSelectObject
{
    public string value { get; set; }
    public string caption { get; set; }
}

2. loadSelect函数是一个辅助方法,它接受一个最初类型为JsonSelectObject的json对象列表,并将其转换为要注入到调用下拉列表中的选项列表。这是来自“jQuery In Action”书籍中的一个很酷的技巧,正如原始代码中引用的那样,它包含在一个名为jquery.jqia.selects.js的文件中,您需要引用它。以下是该js文件中的代码:

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

    $.fn.loadSelect = function(optionsDataArray) {
        return this.emptySelect().each(function() {
            if (this.tagName == 'SELECT') {
                var selectElement = this;
                selectElement.add(new Option("[Select]", ""), null);
                $.each(optionsDataArray, function(index, optionData) {
                    var option = new Option(optionData.caption,
                                  optionData.value);
                    if ($.browser.msie) {
                        selectElement.add(option);
                    }
                    else {
                        selectElement.add(option, null);
                    }
                });
            }
        });
    }

})(jQuery);

这种方法可能比较复杂,但最终你会得到一个干净简洁的代码,在其他地方也可以使用。
希望这对你有所帮助。

更新

在AJAX调用中使用POST而不是GET

您可以用以下代码替换$.getJSON方法,以便使用POST方式进行Ajax调用。

$.post("/City/GetCities/", { country: countryVal }, function(data) {
     citySet.loadSelect(data);
 });

请记得将您的Action方法更改为接受POST请求,通过将[HttpGet]注释更改为[HttpPost],并在Action方法中返回结果时删除JsonRequestBehavior.AllowGet
重要提示:
请注意,我们使用所选项目的值而不是名称。例如,如果用户选择了以下选项。
<option value="US">United States</option>

然后,“US”将被发送到Action方法,而不是“United States”。
更新2:在控制器中访问所选值
假设您的Vehicle视图模型中有以下两个属性:
public string Maker { get; set; }
public string Model { get; set; }

同时,您需要使用与ViewModel属性相同的名称来命名选择元素。

<select id="Maker" name="Maker">
// a List of Countries Options Goes Here.
</select></div>

<select id="Model" name="Model" disabled="disabled">
// To be populated by an ajax call
</select>

然后选择的值将自动绑定到您的ViewModel中,您可以直接在Action方法中访问它们。

如果页面与该ViewModel强类型化,则此操作将起作用。


注意:对于第一个列表(制造商列表),您可以在ViewModel中创建一个类型为SelectList的MakersList,并使用HTML助手自动填充您的Makers下拉列表,其中包含ViewModel中的列表。代码将类似于以下内容:
<%= Html.DropDownListFor(model => model.Maker, Model.MakersList) %>

在这种情况下,此选择器生成的名称也将是“Maker”(ViewModel中属性的名称)。
我希望这就是您要寻找的答案。

我猜我的问题是,如何在控制器中访问这些下拉列表的选定值?是否有一种方法将其绑定到我的视图模型? - Jason
我现在明白你的问题了,我会立即在帖子中更新答案。 - Manaf Abu.Rous
太酷了 - 谢谢。更新DDL真的很复杂。 =/ - Jason
我刚发现了你的问题的一个副本:http://stackoverflow.com/questions/2174334/populate-a-dropdown-select-based-on-the-value-chosen-on-another-dropdown,其中有一个详细示例的链接。如果这对你来说太复杂了,你可能需要去看一下 :) - Manaf Abu.Rous
这是我发表过的最长的答案 :) - Manaf Abu.Rous
显示剩余5条评论

2
最简单的方法是使用jQuery“cascade”插件。您可以看看http://plugins.jquery.com/project/cascade上的演示页面。
如果您想使用ajax解析值,它也会帮助您并消除先前答案中的大量代码,因此您可以集中精力处理逻辑 :)
在谷歌上可以找到许多示例,但最终您只需要以下脚本:
$('#myChildSelect').cascade('#myParentSelect', 
{
    ajax: '/my/url/action',
    template: function(item) {
        return "<option value='" + item.value + "'>" + item.text + "</option>"; },
    match: function(selectedValue) { return this.when == selectedValue; }    
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接