2013-09-21 11 views
9

ビューにコレクションにタイプされたビューがあるとします。 List<ItemViewModel>NameForは、エディタテンプレートのコレクションを反復処理するときに不正な名前を生成します。

@model List<ItemViewModel> 

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

FooBarは、単純に文字列プロパティです。

[i].Foo[i].Barという形式のHTML名前属性を生成します。これはもちろん正しい形式であり、フォームに投稿すると正しくバインドされます。

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

@model WrappingViewModel 

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

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

これは、最初のシナリオでは正常に動作します - ビューは、エディタのテンプレートではありません - と、コレクションではなく、モデル全体よりも、財産です正常に動作します:

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

それはときにのみ失敗しますモデル自体はコレクションです。ビューはエディタテンプレートです。

この回避作業のいくつかの方法がありますが、いずれも理想的です。

  • タイプItemViewModelの個々のインスタンスに、エディタテンプレートは - これは、問題の実際のテンプレートが追加のマークアップが含まれていると良くありませんコレクションに追加する/削除するテンプレート内のコレクション全体を操作できるようにする必要があります。
  • 別のプロパティでを実装するなどして別のプロパティをラップしてテンプレートに渡します。これは不必要なラッピングビューモデルで混乱することのないエンタープライズアプリケーションであるため、これは理想的ではありません。
  • 正しい名前を生成するために、内部エディタテンプレートのマークアップを手動で生成します。これは現在行っていることですが、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のものであろうとしてアクションメソッドに投稿された際に戻って結合しません署名​​。上記のように、Itemsをメインビューまたはに戻した場合は、WrappingViewModelを取り除き、トップレベルモデルをList<ItemViewModel>にしてModelに直接反復すると、これはうまく動作します。この特定のシナリオでのみ失敗します。

+0

ように、コードのその部分をラップする方が良いでしょうか?あなたはどんなパラメーターを期待していますか? –

+0

@AlexanderSimonov少し後で問題を再現するためのソースコードを投稿します。 –

+0

@AlexanderSimonov要求に応じて、完全な再現可能なコードが掲載されています。 –

答えて

5

なぜNameFor(したがってEditorFor)の展示、それはわずかな変化のために正常に動作し、この特定のシナリオでは、この動作(すなわちもしそうなら、なぜ、それは意図的なものであると)ん?

それはバグ(link)であり、それは、ASP.NET MVCのリリースで修正される予定5.

の欠点のいずれかなしにこの現象を回避作業の簡単な方法があります上記?

シンプル:_ItemView.cshtmlエディタテンプレートを削除

@model ItemViewModel 
@Html.EditorFor(m => m.Foo) 
@Html.EditorFor(m => m.Bar) 
    1. は、次のコードでItemViewModel.cshtmlエディタのテンプレートを追加します。

    2. [UIHint("_ItemView")]属性をWrappingViewModelから削除します。少し難しく

    1. は、(上記と同じ)ItemViewModel.cshtmlエディタのテンプレートを追加します。

    2. 修正_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場合

    あなたが持っている代わりに@Html.EditorFor(m => item, null, itemPrefix)のその後オプションのItemViewModel.cshtmlエディタテンプレートを追加したくない場合はそのようなものを書く:

    @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)) 
    

    :あなたは2番目のシナリオのための行動のソースコードを提案でしたこれは、拡張メソッド

  • 関連する問題