NameFor编辑器模板遍历集合时,会产生不正确的名称 [英] NameFor generates incorrect name when iterating over collection in editor template

查看:71
本文介绍了NameFor编辑器模板遍历集合时,会产生不正确的名称的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有输入到一个集合视图,例如一个列表与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。


  

有没有解决此问题的工作没有任何上述的缺点的简单方法?


简单:


  1. 添加 ItemViewModel.cshtml 编辑模板,以下code:

      @model ItemViewModel
    @ Html.EditorFor(M = GT; m.Foo)
    @ Html.EditorFor(M = GT; m.Bar)


  2. 删除 _ItemView.cshtml 编辑模板。


  3. 删除 [UIHint(_ ItemView控件)] 从属性 WrappingViewModel

有点困难:


  1. 添加 ItemViewModel.cshtml 编辑模板(同上)。


  2. 修改 _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 and Bar 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 a List<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 form Items[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 implementing ItemListViewModel) 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 therefore EditorFor) 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 and Bar inputs will be of the form Model.[i].Property and will not bind back when posted to an action method with the signature ActionResult Index(WrappingViewModel). Note that, as mentioned above, this works fine if you iterate over Items in the main view or if you get rid of WrappingViewModel, make the top-level model a List<ItemViewModel> and iterate over Model directly. It only fails for this specific scenario.

解决方案

Why does NameFor (and therefore EditorFor) 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:

  1. Add ItemViewModel.cshtml editor template with following code:

    @model ItemViewModel
    @Html.EditorFor(m => m.Foo)
    @Html.EditorFor(m => m.Bar) 
    

  2. Remove _ItemView.cshtml editor template.

  3. Remove [UIHint("_ItemView")] attribute from WrappingViewModel.

A little bit harder:

  1. Add ItemViewModel.cshtml editor template (same as above).

  2. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆