10

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

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; }
}

在 CreateBook 视图中,我使用了EditorForhelper :

@Html.EditorFor(m => m.Authors, "AuthorSelector")

编辑1:

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>

模板包含一些包装标记,这些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="" />

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

任何想法 ?

谢谢 !

4

6 回答 6

8

我建议您遵守约定,即替换:

@Html.EditorFor(m => m.Authors, "AuthorSelector")

和:

@Html.EditorFor(m => m.Authors)

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

@model AuthorEntryModel
@Html.TextBoxFor(o => o.FirstName)
@Html.TextBoxFor(o => o.LastName)

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


更新:

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

在您的主视图中:

<div class="ptr_authors_wrapper">
    @Html.EditorFor(m => m.Authors)
</div>

在您的编辑器模板中:

@model AuthorEntryModel
<div class="ptr_author_line">
    @Html.TextBoxFor(o => o.FirstName)
    @Html.TextBoxFor(o => o.LastName)
</div>

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

于 2012-07-09T22:01:03.737 回答
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>

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

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

而不是简单地调用:

@Html.EditorFor(m => m[i])
于 2013-12-22T08:10:49.747 回答
2

不知道它是否仍然相关,但此博客涵盖了您的问题的解决方案:http: //btburnett.com/2011/03/correcting-mvc-3-editorfor-template-field-names-when-using-collections .html

--> 感谢itsmatt找到它 :) Jakob

于 2013-01-16T20:07:44.200 回答
1

这是您可以使用的扩展方法,它将呈现部分视图并使用正确的 HTML 字段前缀:

扩展方法

    /// <summary>
    /// Helper method that renders the specified partial view as a HTML-encoded string using the specified
    /// collection as the model, with the intention that the partial view will use an editor template on the items
    /// in the collection.
    /// </summary>
    /// <typeparam name="TModel">the model type</typeparam>
    /// <typeparam name="TProperty">the property type</typeparam>
    /// <param name="htmlHelper">the <see cref="HtmlHelper"/> instance</param>
    /// <param name="partialViewName">the name of the partial view to render</param>
    /// <param name="collectionExpression">the model collection property expression</param>
    /// <returns>the HTML-encoded string</returns>
    public static MvcHtmlString PartialContainingEditorForCollection<TModel, TProperty>
        (this HtmlHelper<TModel> htmlHelper, string partialViewName,
         Expression<Func<TModel, TProperty>> collectionExpression)
        where TProperty : IEnumerable
    {
        var viewData = htmlHelper.ViewContext.ViewData;
        var model = (TModel) viewData.Model;
        var collection = collectionExpression.Compile().Invoke(model);

        var htmlFieldPrefix = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(
            ExpressionHelper.GetExpressionText(collectionExpression));

        return htmlHelper.Partial(partialViewName, collection,
                                  new ViewDataDictionary
                                      {
                                          TemplateInfo = new TemplateInfo {HtmlFieldPrefix = htmlFieldPrefix}
                                      });
    }

示例使用

@Html.PartialContainingEditorForCollection("_TableWithSummary", m => Model.FormModel.ItemsToOrder)
于 2012-08-10T23:04:21.480 回答
0

我还没有找到任何解决此错误的方法,但作为一种解决方法,我UpdateModel通过使用自定义包装类而不是直接使用集合来更改我的方法:

public class BookCreateModel
{
    public string Title { get; set; }
    public int Year { get; set; }
    public BookAuthorsList Authors { get; set; }
}

public class BookAuthorsList
{
    public IList<AuthorEntryModel> AuthorsList { get; set; }
}

public class AuthorEntryModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

因此生成的输入不会再产生命名问题了:)

<input  id="Authors_AuthorsList_0__FirstName" name="Authors.AuthorsList[0].FirstName" type="text"/>
<input  id="Authors_AuthorsList_0__LastName" name="Authors.AuthorsList[0].LastName" type="text"/>
于 2012-07-12T14:23:55.743 回答
0

当我试图找到几乎相同问题的解决方案时,我偶然发现了这一点。我的解决方法是把罐子踢到路上,即。为集合制作包装模型并在编辑器模板中使用它。在提供的情况下,它将是:

public class BookCreateModel
{
    public string Title { get; set; }
    public int Year { get; set; }
    public BookAuthorsModel Authors { get; set; }
}

public class BookAuthorsModel
{
    IList<AuthorEntryModel> Items { get; set; }
}

然后将您的编辑器模板重命名为“BookAuthorsModel.cshtml”并使其如下所示:

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

当你想使用它时,只需调用:

@Html.EditorFor(m => m.Authors)

然后它应该生成如下输入字段:

<input id="Authors_Items_0__FirstName" name="Authors.Items[0].FirstName" type="text" value="" />
<input id="Authors_Items_0__LastName" name="Authors.Items[0].LastName" type="text" value="" />

就我而言,我还适当地更改了 Automapper 映射设置控制器代码。然而,这不适用于一些更复杂的场景,因此可能只是一种解决方法。

于 2015-09-18T17:04:15.827 回答