假设我有一个类型为集合的视图,例如List<ItemViewModel>
:
@model List<ItemViewModel>
@for(int i = 0; i < Model.Count; i++)
{
@Html.EditorFor(m => m[i].Foo)
@Html.EditorFor(m => m[i].Bar)
}
Foo
和Bar
只是字符串属性。
这会生成[i].Foo
和[i].Bar
形式的HTML名称属性,当然,这些属性是正确的,并且在表单中发布时会正确绑定。
现在假设上面的视图是一个编辑器模板,它是这样渲染的(其中Model.Items
是List<ItemViewModel>
):
@model WrappingViewModel
@Html.EditorFor(m => m.Items)
突然之间,编辑器模板中生成的名称的形式 - 例如 - Items.[i].Foo
。默认的模型绑定器无法绑定它,因为它需要表单Items[i].Foo
。
这在第一个场景中运行良好 - 视图不是编辑器模板 - 并且在集合是属性的情况下工作正常,而不是整个模型:
@Html.EditorFor(m => m.Items[i].Foo)
仅当模型本身是集合并且视图是编辑器模板时才会失败。
有几种解决方法,其中没有一种是理想的:
ItemViewModel
的单个实例 - 这并不好,因为相关的实际模板包含用于添加到集合中/从集合中删除的附加标记。我需要能够使用模板中的整个集合。List<ItemViewModel>
包装在另一个属性中(例如通过实现ItemListViewModel
)并将其传递给模板 - 这也不理想,因为这是一个我宁愿不会混淆的企业应用程序多余的包装视图模型。所以,问题是:为什么NameFor
(以及EditorFor
)在这种特殊情况下表现出这种行为,当它适用于轻微变化时(即它是有意的,如果是,为什么) ?有没有一种简单的方法来解决这种行为而没有上述任何缺点?
根据要求,重现的完整代码:
型号:
public class WrappingViewModel
{
[UIHint("_ItemView")]
public List<ItemViewModel> Items { get; set; }
public WrappingViewModel()
{
Items = new List<ItemViewModel>();
}
}
public class ItemViewModel
{
public string Foo { get; set; }
public string Bar { get; set; }
}
控制器操作:
public ActionResult Index()
{
var model = new WrappingViewModel();
model.Items.Add(new ItemViewModel { Foo = "Foo1", Bar = "Bar1" });
model.Items.Add(new ItemViewModel { Foo = "Foo2", Bar = "Bar2" });
return View(model);
}
Index.cshtml:
@model WrappingViewModel
@using (Html.BeginForm())
{
@Html.EditorFor(m => m.Items)
<input type="submit" value="Submit" />
}
_ItemView.cshtml(编辑器模板):
@model List<ItemViewModel>
@for(int i = 0; i < Model.Count; i++)
{
@Html.EditorFor(m => m[i].Foo)
@Html.EditorFor(m => m[i].Bar)
}
Foo
和Bar
输入的名称属性将采用Model.[i].Property
格式,并且在发布到带有签名ActionResult Index(WrappingViewModel)
的操作方法时不会绑定回来。请注意,如上所述,如果您在主视图或中迭代Items
,如果您摆脱WrappingViewModel
,则可以正常工作,将顶级模型设为{ {1}}并直接迭代List<ItemViewModel>
。它只适用于这种特定情况。
答案 0 :(得分:5)
为什么
NameFor
(以及EditorFor
)在这种特殊情况下表现出这种行为,当它适用于轻微变化时(即它是有意的,如果是,为什么)?
这是一个错误(link),它将在ASP.NET MVC 5发布时修复。
是否有一种解决此问题的简单方法,而没有上述任何缺点?
<强>简单:强>
使用以下代码添加ItemViewModel.cshtml
编辑器模板:
@model ItemViewModel
@Html.EditorFor(m => m.Foo)
@Html.EditorFor(m => m.Bar)
删除_ItemView.cshtml
编辑器模板。
[UIHint("_ItemView")]
删除WrappingViewModel
属性。稍微努力一点:
添加ItemViewModel.cshtml
编辑器模板(与上述相同)。
修改_ItemView.cshtml
:
@model List<ItemViewModel>
@{
string oldPrefix = ViewData.TemplateInfo.HtmlFieldPrefix;
try
{
ViewData.TemplateInfo.HtmlFieldPrefix = string.Empty;
for (int i = 0; i < Model.Count; i++)
{
var item = Model[i];
string itemPrefix = string.Format("{0}[{1}]", oldPrefix, i.ToString(CultureInfo.InvariantCulture));
@Html.EditorFor(m => item, null, itemPrefix)
}
}
finally
{
ViewData.TemplateInfo.HtmlFieldPrefix = oldPrefix;
}
}
<强>更新强>
如果您不想为 second 选项添加ItemViewModel.cshtml
编辑器模板,那么您需要编写类似的内容而不是@Html.EditorFor(m => item, null, itemPrefix)
:
@Html.EditorFor(m => item.Foo, null, Html.NameFor(m => item.Foo).ToString().Replace("item", itemPrefix))
@Html.EditorFor(m => item.Bar, null, Html.NameFor(m => item.Bar).ToString().Replace("item", itemPrefix))
注意:最好将这段代码包装为扩展方法