9

コレクションに型付けされたビューがあるとします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およびBar単純な文字列プロパティです。

[i].Fooこれにより、フォームおよびの HTML name 属性が生成されます[i].Bar。もちろん、これは正しく、フォームに投稿されたときに正しくバインドされます。

代わりに、上記のビューが次のようにレンダリングされるエディター テンプレートであるとします (Model.ItemsList<ItemViewModel>):

@model WrappingViewModel

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

突然、エディター テンプレート内で生成された名前は、たとえば - という形式になりItems.[i].Fooます。デフォルトのモデル バインダーは、 form を想定しているため、これをバインドできませんItems[i].Foo

これは、最初のシナリオ (ビューがエディター テンプレートではない) でうまく機能し、コレクションがモデル全体ではなくプロパティである場合にもうまく機能します。

@Html.EditorFor(m => m.Items[i].Foo)

モデル自体がコレクション、ビューがエディター テンプレートの場合にのみ失敗します。

これを回避するにはいくつかの方法がありますが、どれも理想的ではありません。

  • の個々のインスタンスにエディター テンプレートを入力しますItemViewModel。問題の実際のテンプレートには、コレクションに追加/コレクションから削除するための追加のマークアップが含まれているため、これは適切ではありません。テンプレート内でコレクション全体を操作できる必要があります。
  • List<ItemViewModel>を別のプロパティにラップして(たとえば、 を実装することによりItemListViewModel)、それをテンプレートに渡します。これも理想的ではありません。これは、余分なラッピング ビュー モデルで混乱させたくないエンタープライズ アプリケーションであるためです。
  • 内部エディター テンプレートのマークアップを手動で生成して正しい名前を生成します。これは私が現在行っていることですが、HtmlHelpers の柔軟性が失われるため、むしろ避けたいと思います。

では、問題は、この特定のシナリオで、わずかな変動で問題なく動作するのに、なぜNameFor(したがってEditorFor) がこの動作を示すのか (つまり、意図的なものなのか、そうであればなぜ) ? 上記の欠点なしでこの動作を回避する簡単な方法はありますか?

要求に応じて、再現する完全なコード:

モデル:

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

コントローラーのアクション:

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

インデックス.cshtml:

@model WrappingViewModel

@using (Html.BeginForm())
{
    @Html.EditorFor(m => m.Items)
    <input type="submit" value="Submit" />
}

_ItemView.cshtml (エディター テンプレート):

@model List<ItemViewModel>

@for(int i = 0; i < Model.Count; i++)
{
    @Html.EditorFor(m => m[i].Foo)
    @Html.EditorFor(m => m[i].Bar)
}

FooおよびBar入力の名前属性はフォームModel.[i].Propertyになり、署名付きのアクション メソッドにポストされたときにバインドされませんActionResult Index(WrappingViewModel)。上記のようにItems、メイン ビューで反復処理を行う場合、またはWrappingViewModelを削除する場合は、トップレベル モデルを aにして直接List<ItemViewModel>反復処理を行うと、これは正常に機能することに注意してください。Modelこの特定のシナリオでのみ失敗します。

4

1 に答える 1