Asp.net Mvc 复杂类型模型绑定之列表属性绑定方法及注意点
模型绑定的功能说明和表单键名的约定请见:
ASP.NET MVC 模型绑定的功能和问题
本文记录几个注意点,请先观察示例代码。
模型
public class DesignerViewModel
{
//其他基本属性
...
/// <summary>
/// 设计风格
/// </summary>
[DisplayName("设计风格")]
[Required(ErrorMessage = "请选择至少一个设计风格")]
public List<CategoryStyle> CaseStyles { get; set; }
//CategoryStyle 仅含一个CategoryStyleID属性和Name属性
}
控制器
public ActionResult Add()
{
ViewBag.CaseStylesSelect = styleRepo.GetAll();
return View();
}
public ActionResult Add(DesignerViewModel model)
{
if (ModelState.IsValid)
{
//do sth
}
ViewBag.CaseStylesSelect = styleRepo.GetAll();
return View(model);
}
视图
@{
List<CategoryStyle> styles = (List<CategoryStyle>)ViewBag.CaseStylesSelect;
for (int i = 0; i < styles.Count(); i++)
{
<span class="col-sm-3">
<input type="checkbox"
id="CaseStyles[@i].CategoryStyleId"
name="CaseStyles[@i].CategoryStyleId"
value="@(styles[i].CategoryStyleId)"
@((Model != null && Model.CaseStyles.Any(c => c.CategoryStyleId == styles[i].CategoryStyleId)) ? "checked" : "") />
<input type="hidden" name="CaseStyles[@i].CategoryStyleId" value="0" />
<label for="CaseStyles[@i].CategoryStyleId">@(styles[i].Name)</label>
</span>
}
}
注意点:
- 提供默认值的hidden不能省;
- 提供默认值的hidden不能放在checkbox之前;
- 提供默认值的hidden的值必须和绑定对象的属性类型匹配。
关于第一点,按模型绑定的约定,这里要求name属性必须为CaseStyles[0].CategoryStyleId形式,但是这个索引很关键,提交的时候必须连续。 如果我们不提供hidden默认值,那么浏览器在提交checkbox的时候,只会提交选中的项(这也是为什么Html.Checkbox辅助方法会生成一个默认值为false的hidden,除非绑定的时候属性是可空类型,否则会抛异常),那么你会发现绑定到模型上的,只有从索引0开始的连续几项,如果你没选中第0项,甚至后面的都无法绑定上去。所以必须提供hidden默认值以提交连续的CaseStyles[i].CategoryStyleId。
第二点,如果hidden放在checkbox之前,提交表单的时候也会看到hidden的键值对在同名的checkbox之前,绑定的时候会使用前面这个健值对,而忽略后面这个同名键值对。这个估计是模型绑定在遇到表单同名键值对时候的默认行为。
第三点,这里我要绑定的是一个id,是int型,一开始我考虑到Html.CheckBox的情况,直接使用了一个值为false的hidden,发现绑定有异常,改为同为int的0即可。