2011-12-14 13 views
1

サーバーからデータを正しく受信したにもかかわらず、MVC3カミソリのフォームがforeachコードブロック内に重複した値をレンダリングすることは興味深いことでした。ここに私の単純な形式は... MVC3カミソリでMVC3剃刀「For」モデル - 内容が複製された

ある - 戻り上記のアクションを保存

[HttpPost] 
    public ActionResult Save(ViewModel.CategoryForm cat) 
    { 
     ... save the data based on posted "cat" values (I correctly receive them here) 

     List<Category> cL = ... populate category list here 
     return View(cL); 
    } 

- 私の.cshtmlページのサンプル

@model List<Category> 
@using (@Html.BeginForm("Save", "Categories", FormMethod.Post)) 
{ 
foreach (Category cat in Model) 
{ 
     <span>Test: @cat.CategoryName</span> 

     <span>Actual: @Html.TextBoxFor(model => cat.CategoryName)</span> 
     @Html.HiddenFor(model => cat.ID) 
     <p>---</p>    
} 

    <input type="submit" value="Save" name="btnSaveCategory" id="btnSaveCategory" /> 
} 

私のコントローラのアクションは、次のようになります正しいデータを持つモデル。

上記フォームを送信した後、私はアクションが完了すると、次のようなカテゴリの値を見ることを期待...

Test: Category1, Actual:Category1 
Test: Category2, Actual:Category2 
Test: Category3, Actual:Category3 
Test: Category4, Actual:Category4 

しかし@Html.TextBoxForは、リストから最初の値を複製します。フォームを投稿した後、以下のような応答が表示されます。 「実際」の値は、サーバーから正しいデータを取得しても繰り返されます。

Test: Category1, Actual:Category1 
Test: Category2, Actual:Category1 
Test: Category3, Actual:Category1 
Test: Category4, Actual:Category1 

私は間違っていますか?どんな助けもありがとう。

答えて

3

TextBoxForのようなヘルパーメソッドは、オブジェクトのコレクションではなく単一のオブジェクトを表すViewModelで使用するためのものです。

通常の使用は次のようになりますcをメソッド内、マッピングされる

@Html.TextBoxFor(c => c.Name) 

ViewData.Modelします。あなたは何か違うことをやっている

@Html.TextBoxFor(c => iterationItem.Name) 

方法internallはまだレンダリング用のベースオブジェクトとしてViewData.Modelを使用しようとしますが、反復項目でそれを使用する予定。この構文は、コンパイラには有効ですが、この問題を引き起こします。

回避策は、単一の項目に対して動作する部分的なビューを作成することです。そのビューの中で正しい構文(最初のサンプル)でhtmlヘルパーを使用してから、iteration項目をパラメータとして渡してforeach内で呼び出します。それは正しく動作するはずです。

0

これを行うより良い方法は、EditorTemplatesを使用することです。

あなたがこれを行うだろうフォームで

@model List<Category> 
@using (@Html.BeginForm("Save", "Categories", FormMethod.Post)) 
{ 
    @Html.EditorForModel() 
    <input type="submit" value="Save" name="btnSaveCategory" id="btnSaveCategory" /> 
} 

その後、あなたは〜/ビュー/共有フォルダまたはあなたのコントローラのいずれかで、EditorTemplatesというフォルダを作成しますが、あなたがしたいかに応じて(フォルダを見ます全アプリまたは単にこのコントローラ)でテンプレートを共有し、EditorTemplatesフォルダに、次のようになりますCategory.cshtmlファイルを作成します。

@model Category 
<span>Test: @Model.CategoryName</span> 

<span>Actual: @Html.TextBoxFor(model => model.CategoryName)</span> 
@Html.HiddenFor(model => model.ID) 
<p>---</p>    

MVCは自動的にコレクションを反復処理し、用テンプレートを呼び出しますその中の各項目。

0

私は、ビュー内でforeachループを使用すると、コレクション内のすべてのアイテムについて同じテキストボックスの名前属性が表示されることに気付きました。あなたの例では、すべてのテキストボックスには、次のIDでレンダリングされ、名前の属性:あなたのコントローラは、フォームデータの収集を受け取ると

<input id="cat_CategoryName" name="cat.CategoryName" value="Category1" type="text"> 

、別の値としてコレクションを再構築することができません。

ソリューション

  1. 私が採用してきた良好なパターンは、あなたが戻って投稿したい同じクラスにあなたのビューをバインドすることです。この例では、モデルはList<Category>にバインドされていますが、コントローラのSaveメソッドはモデルViewModel.CategoryFormを受け取ります。私はそれらを同じにするでしょう。

  2. foreachの代わりにforループを使用してください。 name/id属性は一意であり、モデルバインダーは値を区別することができます。

私の最終的なコード:

ビュー

@model CategoryForm 
@using TestMvc3.Models 

@using (@Html.BeginForm("Save", "Categories", FormMethod.Post)) 
{ 
    for (int i = 0; i < Model.Categories.Count; i++) 
    { 
     <span>Test: @Model.Categories[i].CategoryName</span> 

     <span>Actual: @Html.TextBoxFor(model => Model.Categories[i].CategoryName)</span> 
     @Html.HiddenFor(model => Model.Categories[i].ID) 
     <p>---</p>    
    } 

    <input type="submit" value="Save" name="btnSaveCategory" id="btnSaveCategory" /> 
} 

コントローラ

public ActionResult Index() 
{ 
    // create the view model with some test data 
    CategoryForm form = new CategoryForm() 
    { 
     Categories = new List<Category>() 
    }; 

    form.Categories.Add(new Category() { ID = 1, CategoryName = "Category1" }); 
    form.Categories.Add(new Category() { ID = 2, CategoryName = "Category2" }); 
    form.Categories.Add(new Category() { ID = 3, CategoryName = "Category3" }); 
    form.Categories.Add(new Category() { ID = 4, CategoryName = "Category4" }); 

    // pass the CategoryForm view model 
    return View(form); 
} 

[HttpPost] 
public ActionResult Save(CategoryForm cat) 
{ 
    // the view model will now have the correct categories 
    List<Category> cl = new List<Category>(cat.Categories); 

    return View("Index", cat); 
} 
関連する問題