10

Title、PublishYearなどの本の平面情報に加えて、本の著者のコレクション(複合型)で構成されるBookCreateModelがあります。

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 ビューでEditorForヘルパーを使用しました:

@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レンダリングされた各アイテムのインデックスを認識する必要があるいくつかのラッパー マークアップと、子入力の相互作用を処理し、(テンプレート内で) 1 回レンダリングする必要があるいくつかの 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

ほぼ同じ問題の解決策を見つけようとしていたときに、これに出くわしました。私の回避策は、缶を道に蹴り飛ばすことでした。コレクションのラッパー モデルを作成し、それをエディター テンプレートで使用します。提供された場合、それは次のようになります。

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 マッピング設定も適切に変更しました Controller コード。ただし、これはより複雑なシナリオでは使用できないため、おそらく単なる回避策です。

于 2015-09-18T17:04:15.827 に答える
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 に答える