将集合传递给EditorFor()时,它会为输入元素生成无效的名称

Ali*_*uri 10 asp.net-mvc model-binding editorfor razor asp.net-mvc-3

我有一个BookCreateModel,它包含书籍的平面信息,例如Title,PublishYear&etc以及书籍作者集合(复杂类型):

public class BookCreateModel
{
    public string Title { get; set; }
    public int Year { get; set; }
    public IList<AuthorEntryModel> Authors { get; set; }
}

public class AuthorEntryModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

在CreateBook视图中我使用了EditorForhelper:

@Html.EditorFor(m => m.Authors, "AuthorSelector")
Run Code Online (Sandbox Code Playgroud)

EDIT1:

和AuthorSelector模板如下:

<div class="ptr_authors_wrapper">
    @for (int i = 0; i < Model.Count; i++)
    {
       <div class="ptr_author_line" data-line-index="@i">
        @Html.TextBoxFor(o => o[i].FirstName)
        @Html.TextBoxFor(o => o[i].LastName)
       </div>
    }
</div>
<script>
     ...
</script>
Run Code Online (Sandbox Code Playgroud)

AuthorSelector模板包含需要知道每个渲染项目的索引加上一些javascript其处理孩子输入的交互,需要进行一次渲染(里面的一些包装标记AuthorSelector模板),从而摆脱的for循环/或AuthorSelector模板不可能.

现在问题是EditorFor有点奇怪并生成如下输入名称:

<input id="Authors__0__FirstName" name="Authors.[0].FirstName" type="text" value="" />
<input id="Authors__0__LastName" name="Authors.[0].LastName" type="text" value="" />
Run Code Online (Sandbox Code Playgroud)

正如你所看到的那样,不是生成名称,Authors[0].FirstName而是添加一个额外的点,这使得默认模型绑定器无法解析发布的数据.

任何的想法 ?

谢谢 !

Dar*_*rov 8

我建议你坚持惯例,即替换:

@Html.EditorFor(m => m.Authors, "AuthorSelector")
Run Code Online (Sandbox Code Playgroud)

有:

@Html.EditorFor(m => m.Authors)
Run Code Online (Sandbox Code Playgroud)

然后重命名你~/Views/Shared/EditorTemplates/AuthorSelector.cshtml~/Views/Shared/EditorTemplates/AuthorEntryModel.cshtml并将其强类型化为单个AuthorEntryModel模型并摆脱循环:

@model AuthorEntryModel
@Html.TextBoxFor(o => o.FirstName)
@Html.TextBoxFor(o => o.LastName)
Run Code Online (Sandbox Code Playgroud)

ASP.NET MVC将自动为集合的所有元素呈现编辑器模板并生成专有名称.


更新:

在看到您的更新后,我的回复是:

在您的主视图中:

<div class="ptr_authors_wrapper">
    @Html.EditorFor(m => m.Authors)
</div>
Run Code Online (Sandbox Code Playgroud)

在您的编辑器模板中:

@model AuthorEntryModel
<div class="ptr_author_line">
    @Html.TextBoxFor(o => o.FirstName)
    @Html.TextBoxFor(o => o.LastName)
</div>
Run Code Online (Sandbox Code Playgroud)

你会注意到模板中缺少脚本是完全正常的.脚本与标记无关.他们进入单独的JavaScript文件.在这个文件中,您可以使用jQuery来执行标记所需的任何操作.它为您提供了一些方法.index(),允许您在匹配的选择器中获取元素的索引,这样您就不需要编写任何循环并使用data-line-index属性之类的东西污染您的标记.


Zac*_*tin 7

我参加派对有点晚了,但希望这对某人有所帮助.

向下挖掘System.Web.Mvc.Html.DefaultEditorTemplates.CollectionTemplate(HtmlHelper html, TemplateHelpers.TemplateHelperDelegate templateHelper),框架的默认模板通过临时设置HtmlFieldPrefix为空字符串并显式地将前缀和索引传递给调用来处理此问题EditorFor().

<div class="ptr_authors_wrapper">
@{
    var prefix = ViewData.TemplateInfo.HtmlFieldPrefix;

    ViewData.TemplateInfo.HtmlFieldPrefix = String.Empty;

    for (int i = 0; i < Model.Count; i++)
    {
        <div class="ptr_author_line" data-line-index="@i">
            @* You can also use null instead of "TextBox" to let the framework resolve which editor to use. *@
            @Html.EditorFor(o => o[i].FirstName, "TextBox", String.Format("{0}[{1}].FirstName", prefix, i))
            @Html.EditorFor(o => o[i].LastName, "TextBox", String.Format("{0}[{1}].LastName", prefix, i))
        </div>
    }

    ViewData.TemplateInfo.HtmlFieldPrefix = prefix;
}
</div>
<script>
     ...
</script>
Run Code Online (Sandbox Code Playgroud)

我发现当框架[0].Children.[0].ChildProperty因为Children集合的命名模板而编写名称时,这特别有用.就我而言,解决方案是致电:

@Html.EditorFor(m => m[i], null, String.Format("{0}[{1}]", prefix, i))
Run Code Online (Sandbox Code Playgroud)

而不是简单地调用:

@Html.EditorFor(m => m[i])
Run Code Online (Sandbox Code Playgroud)