MVC下拉列表没有拿起所选项目?

时间:2016-02-29 04:01:32

标签: asp.net-mvc html-select model-binding

我的模型包含一系列邮政编码项目(IEnumerable<SelectListItem>)。 它还包含一系列选定的邮政编码(string[])。

在我的HTML页面中,我想将每个选定的邮政编码渲染为包含所有邮政编码选项的下拉列表。我的第一次尝试不起作用:

@foreach (var zip in Model.ZipCodes) {
    Html.DropDownList( "ZipCodes", Model.ZipCodeOptions )
}

我意识到虽然这会产生具有正确“name”属性的下拉,但它不知道ZipCodes的哪个元素保存该特定框的值,并且可能只是默认为第一个。

我的第二次尝试让我感到惊讶。我明确地将正确的SelectListItem的Selected属性设置为true,并且它仍然呈现没有选择任何内容的控件:

@foreach (var zip in Model.ZipCodes) {
    Html.DropDownList( "ZipCodes", Model.ZipCodeOptions.Select( x => (x.Value == zip) ? new SelectListItem() { Value = x.Value, Text = x.Text, Selected = true } : x ) )
}

在那里,它返回一个包含所有原始项的新IEnumerable<SelectListitem>,除非它是所选项,在这种情况下,该元素是一个新的SelectListItem,其Selected属性设置为true。在最终输出中,该属性根本不受尊重。

我的最后一次尝试是尝试对我想用作值的字符串元素使用显式索引:

@{int zipCodeIndex = 0;}
@foreach (var zip in Model.ZipCodes) {
    Html.DropDownList( "ZipCodes[" + (zipCodeIndex++) + "]", Model.ZipCodeOptions )
}

这也不起作用,可能因为名称不再是“ZipCodes”,而是“ZipCodes [x]”。我最初还收到了某种只读收集错误,并且必须将ZipCodes属性的类型从string[]更改为List<string>

在第四次尝试中,我尝试了以下内容:

@for (int zipCodeIndex = 0; zipCodeIndex < Model.ZipCodes.Count; zipCodeIndex++)
{
    var zip = Model.ZipCodes[zipCodeIndex];
    Html.DropDownListFor( x => x.ZipCodes[zipCodeIndex], Model.ZipCodeOptions )
}

这会产生id为“ZipCodes_1_”的控件和名称如“ZipCodes [1]”,但不会选择正确的值。如果我显式设置了正确项目的Selected属性,那么这将起作用:

@for (int zipCodeIndex = 0; zipCodeIndex < Model.ZipCodes.Count; zipCodeIndex++)
{
    var zip = Model.ZipCodes[zipCodeIndex];
    Html.DropDownListFor( x => x.ZipCodes[zipCodeIndex], Model.ZipCodeOptions.Select( x => (x.Value == zip) ? new SelectListItem() { Value = x.Value, Text = x.Text, Selected = true } : x ) ) 
}

然而,这种方法的问题在于,如果我在JavaScript中添加一个新的下拉列表并给它们所有名称“ZipCodes”,那么那些完全覆盖所有显式索引的,它们永远不会进入服务器。它似乎不喜欢将简单的“ZipCodes”名称与显式数组元素“ZipCodes [1]”混合,即使它们仅在使用任何一个变量时映射到同一个变量。

在U.I.中,用户可以单击按钮添加新的下拉列表并选择另一个邮政编码。它们都被命名为ZipCodes,因此它们都被发布到ZipCodes数组。在上面循环中渲染字段时,我希望它在给定索引处读取属性的值,但这不起作用。我甚至尝试重新映射SelectListItems,以便正确选项的“Selected”属性为true,但它仍然呈现没有选择任何内容的控件。出了什么问题?

1 个答案:

答案 0 :(得分:1)

前两个代码段不起作用的原因是ZipCodes是模型中的属性,而它的属性值决定了所选内容(不在{2}中设置selected值忽略的SelectList构造函数)。由于ZipCodes的值是一个值数组,而不是与其中一个选项值匹配的单个值,因此找不到匹配项,因此选择了第一个选项(因为必须存在某些选项)。请注意,在内部,辅助方法会根据您提供的方法生成新的IEnumerable<SelectListItem>,并根据模型值设置selected属性。

第3和第4个代码段不起作用的原因是由于已知使用DropDownListFor()方法的限制,并且为了使其有效,您需要使用EditorTemplate并传递{使用SelectList对模板{1}},或者在循环的每次迭代中构造一个新的AdditionalViewData(根据您的上次尝试)。请注意,它需要的只是

SelectList

如果您想使用for(int i = 0; i < Model.ZipCodes.Length; i++) { @Html.DropDownListFor(m => m.ZipCodes[i], new SelectList(Model.ZipCodeOptions, "Value", "Text", Model.ZipCodes[i])) } 方法为每个<select>元素仅使用一个公用名(不带索引器),那么它需要是一个与模型属性不匹配的名称,例如

DropDownList()

然后在POST方法中添加一个额外的参数foreach(var item in Model.ZipCodes) { @Html.DropDownList("SelectedZipCodes", new SelectList(Model.ZipCodeOptions, "Value", "Text", item)) } 来绑定值。

或者,使用上面的string[] SelectedZipCodes循环和for方法,但包含DropDownListFor()的隐藏输入,该输入允许提交基于非零的非连续收集项目控制器并修改脚本以使用this answer

中显示的技术添加新项目

请注意indexer

中显示EditorTemplateAdditionalViewData一起使用的示例