NameFor编辑器模板遍历集合时,会产生不正确的名称 [英] NameFor generates incorrect name when iterating over collection in editor template
问题描述
假设我有输入到一个集合视图,例如一个列表与LT; ItemViewModel>
:
@model名单< ItemViewModel>@for(INT I = 0; I< Model.Count;我++)
{
@ Html.EditorFor(M = GT; M []包含.foo)
@ Html.EditorFor(M = GT; M [] .BAR)
}
富
和酒吧
只是字符串属性。
这生成表单的HTML属性的名称 [I]包含.foo
和 [I] .BAR
,其中当然,在一个表单提交的时候是正确的,正确地结合
现在假设,相反,认为上述观点是编辑模板,呈现像这样(其中 Model.Items
是列表< ItemViewModel>
)
@model WrappingViewModel@ Html.EditorFor(M = GT; m.Items)
突然,编辑模板内产生的名称的形式 - 例如 - 项目[I]包含.foo
。因为它预计形式默认模型粘结剂不能绑定这个项目[I]包含.foo
。
这工作在第一种情况下很好 - 那里的看法是不是一个编辑模板 - 还有那里的收集是一个性质,工作正常,而不是整个模型:
@ Html.EditorFor(M = GT; m.Items [I]包含.foo)
在模式本身是一个集合,只失败的和的观点是编辑模板。
有解决这个工作的一些方法,其中没有一个是理想的:
- 键入编辑模板
ItemViewModel
的单个实例 - 这是没有好,因为有问题的实际的模板包含用于添加到/从集合删除额外的标记。我需要能够与模板内的整个集合工作。 - 裹在
列表< ItemViewModel>
在另一个属性(例如,通过实施ItemListViewModel
),并传递给模板 - 这是不理想,要么,因为这是一个企业应用程序,我宁可不使用多余的包装视图模型杂乱 。 - 生成用于内部编辑模板手工制作的正确名称的标记 - 这是我目前做什么,但我宁愿避免它,因为我失去了HtmlHelpers的灵活性
因此,问题:为什么 NameFor
(因此 EditorFor
)的展览在这个特定的情况下,当这种行为它的细微变化(即它是故意的,如果是这样,为什么)工作正常?是否有解决此问题的工作没有任何上述的缺点的简单方法?
根据要求,充分code复制:
模型:
公共类WrappingViewModel
{
[UIHint(_ ItemView控件)]
公开名单< ItemViewModel>项目{搞定;组; } 公共WrappingViewModel()
{
项目=新的List< ItemViewModel>();
}
}公共类ItemViewModel
{
公共字符串美孚{搞定;组; }
公共字符串酒吧{搞定;组; }
}
控制器动作:
公众的ActionResult指数()
{
VAR模型=新WrappingViewModel();
model.Items.Add(新ItemViewModel {美孚=Foo1,酒吧=BAR1});
model.Items.Add(新ItemViewModel {美孚=foo2的,酒吧=条2});
返回查看(模型);
}
Index.cshtml:
@model WrappingViewModel@using(Html.BeginForm())
{
@ Html.EditorFor(M = GT; m.Items)
<输入类型=提交值=提交/>
}
_ItemView.cshtml(编辑模板):
@model名单< ItemViewModel>@for(INT I = 0; I< Model.Count;我++)
{
@ Html.EditorFor(M = GT; M []包含.foo)
@ Html.EditorFor(M = GT; M [] .BAR)
}
名称的属性富
和酒吧
的投入将是形式模型。[我] .Property
以及何时发布到与签名的操作方法将不绑定回的ActionResult指数(WrappingViewModel)
。需要注意的是,如上面提到的,这个如果你遍历在主视图项
工作正常的或的,如果你摆脱 WrappingViewModel
,使顶层模型列表< ItemViewModel>
和迭代模式
直接。它只是失败这种特定情况下。
为什么
NameFor
(因此EditorFor
)的展览在这个特定的情况下这种行为,当它工作正常进行轻微变化(即它是故意的,如果是这样,为什么)?
块引用>这是一个bug(链接),它会被固定释放ASP.NET MVC 5。
有没有解决此问题的工作没有任何上述的缺点的简单方法?
块引用>简单:
添加
ItemViewModel.cshtml
编辑模板,以下code:@model ItemViewModel
@ Html.EditorFor(M = GT; m.Foo)
@ Html.EditorFor(M = GT; m.Bar)
删除
_ItemView.cshtml
编辑模板。- 删除
[UIHint(_ ItemView控件)]
从属性WrappingViewModel
。有点困难:
添加
ItemViewModel.cshtml
编辑模板(同上)。修改
_ItemView.cshtml
:@model名单< ItemViewModel>@ {
字符串old preFIX = ViewData.TemplateInfo.HtmlField preFIX; 尝试
{
ViewData.TemplateInfo.HtmlField preFIX =的String.Empty; 的for(int i = 0; I< Model.Count;我++)
{
VAR项目=模型[I]
字符串项preFIX =的String.Format({0} [{1}],老preFIX,i.ToString(CultureInfo.InvariantCulture));
@ Html.EditorFor(M = GT;项目,空项目preFIX)
}
}
最后
{
ViewData.TemplateInfo.HtmlField preFIX =旧preFIX;
}
}
更新
在,如果你不希望添加
ItemViewModel.cshtml
编辑模板进行的第二的选项,然后代替@ Html.EditorFor(M = GT;项目,空项目preFIX)
你必须写类似的东西:@ Html.EditorFor(M = GT; item.Foo,空,Html.NameFor(M =方式> item.Foo)的ToString()更换(项目,项目preFIX))@ Html.EditorFor(M = GT; item.Bar,空,Html.NameFor(M = GT; item.Bar)的ToString()更换(项目,项目preFIX)。)
的注意的:这是更好地包装那块code作为扩展方法
Suppose I have a view typed to a collection, e.g. a
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
andBar
are simply string properties.This generates HTML name attributes of the form
[i].Foo
and[i].Bar
, which, of course, is correct and binds correctly when posted in a form.Now suppose, instead, that the above view is an editor template, which is rendered like so (where
Model.Items
is aList<ItemViewModel>
):@model WrappingViewModel @Html.EditorFor(m => m.Items)
All of a sudden, the names generated within the editor template are of the form - for example -
Items.[i].Foo
. The default model binder can't bind this as it expects the formItems[i].Foo
.This works fine in the first scenario - where the view is not an editor template - and also works fine where the collection is a property, rather than the entire model:
@Html.EditorFor(m => m.Items[i].Foo)
It only fails when the model itself is a collection and the view is an editor template.
There are a few ways of working around this, none of which are ideal:
- Type the editor template to an individual instance of
ItemViewModel
- this is no good as the actual template in question contains additional markup for adding to/removing from the collection. I need to be able to work with the entire collection inside the template.- Wrap the
List<ItemViewModel>
in another property (e.g. by implementingItemListViewModel
) and pass that to the template - this is not ideal, either, as this is an enterprise application that I would rather not clutter with superfluous wrapping view models.- Generate the markup for the inner editor templates manually to produce the correct name - this is what I am currently doing but I would rather avoid it as I lose the flexibility of HtmlHelpers.
So, the question: Why does
NameFor
(and thereforeEditorFor
) exhibit this behaviour in this particular scenario when it works fine for slight variations (i.e. is it intentional and, if so, why)? Is there a simple way of working around this behaviour without any of the shortcomings of the above?As requested, full code to reproduce:
Models:
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; } }
Controller action:
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 (editor template):
@model List<ItemViewModel> @for(int i = 0; i < Model.Count; i++) { @Html.EditorFor(m => m[i].Foo) @Html.EditorFor(m => m[i].Bar) }
Name attributes for
Foo
andBar
inputs will be of the formModel.[i].Property
and will not bind back when posted to an action method with the signatureActionResult Index(WrappingViewModel)
. Note that, as mentioned above, this works fine if you iterate overItems
in the main view or if you get rid ofWrappingViewModel
, make the top-level model aList<ItemViewModel>
and iterate overModel
directly. It only fails for this specific scenario.解决方案Why does
NameFor
(and thereforeEditorFor
) exhibit this behaviour in this particular scenario when it works fine for slight variations (i.e. is it intentional and, if so, why)?It is a bug (link) and it will be fixed with release of ASP.NET MVC 5.
Is there a simple way of working around this behaviour without any of the shortcomings of the above?
Simple:
Add
ItemViewModel.cshtml
editor template with following code:@model ItemViewModel @Html.EditorFor(m => m.Foo) @Html.EditorFor(m => m.Bar)
Remove
_ItemView.cshtml
editor template.- Remove
[UIHint("_ItemView")]
attribute fromWrappingViewModel
.A little bit harder:
Add
ItemViewModel.cshtml
editor template (same as above).Modify
_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; } }
UPDATE
In case if you don't want to add
ItemViewModel.cshtml
editor template for the second option then instead of@Html.EditorFor(m => item, null, itemPrefix)
you have to write something like that:@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))
NOTE: It's better to wrap that piece of code as extension method
这篇关于NameFor编辑器模板遍历集合时,会产生不正确的名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!