2012-09-11 9 views
9

Razorを使用して、MVCアプリケーションの編集ページがあります。Razorを使用してASP.NET MVC 4でListプロパティを持つオブジェクトの編集フォームを作成するには

私のようなモデルがあります:

public class MyModelObject 
{ 
    public int Id { get; set; } 

    public string Name { get; set; } 

    public string Description { get; set; } 

    public List<MyOtherModelObject> OtherModelObjects { get; set; } 
} 

をそしてMyOtherModelObjectは、次のようになります。私はMyModelObjectの編集ページを作っています

public class MyOtherModelObject 
{ 
    public string Name { get; set; } 

    public string Description { get; set; } 
} 

。 MyModelObjectのEditページのフォームにスペースを追加して、ユーザーがOtherModelObjectsのListを希望する数のMyOtherModelObjectインスタンスを作成または追加できるようにする必要があります。

私はユーザーがボタンを押すことができると考えています。フォーム要素のPartialViewを返す別のアクションにajaxを実行します(これは編集ページのフォームに含まれるためフォームタグなし)。ユーザーが必要なすべてのMyOtherModelObjectを追加してデータを記入すると、その編集内容を既存のMyModelObjectに保存することができます.HttpPostからEditアクションが実行され、すべてのMyOtherModelObjectsが正しいリストに保存されます。

また、アイテムを追加したユーザーは、アイテムを再注文する必要があります。

誰でもこの作業を行う方法を知っていますか?サンプルプロジェクト、またはこのソリューションによるオンラインサンプルウォークスルーを実装しましたか?

+0

http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx(Razorの構文もMVC4の可能性もありますがアイデア) –

+0

一見すると。それはリストのために働くように見えますが、別のオブジェクトの一部であるListに対しても機能し、モデルをバインドできますか? – DaveH

答えて

19

このblog postには、ステップバイステップガイドが記載されています。


UPDATE:私はあなたのシナリオに上記の記事を適合させる方法をステップバイステップを説明していますコメント欄で要求されるように

モデル:

public class MyOtherModelObject 
{ 
    public string Name { get; set; } 
    public string Description { get; set; } 
} 

public class MyModelObject 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public string Description { get; set; } 
    public List<MyOtherModelObject> OtherModelObjects { get; set; } 
} 

コントローラー:

public class HomeController : Controller 
{ 
    public ActionResult Index() 
    { 
     var model = new MyModelObject 
     { 
      Id = 1, 
      Name = "the model", 
      Description = "some desc", 
      OtherModelObjects = new[] 
      { 
       new MyOtherModelObject { Name = "foo", Description = "foo desc" }, 
       new MyOtherModelObject { Name = "bar", Description = "bar desc" }, 
      }.ToList() 
     }; 
     return View(model); 
    } 

    [HttpPost] 
    public ActionResult Index(MyModelObject model) 
    { 
     return Content("Thank you for submitting the form"); 
    } 

    public ActionResult BlankEditorRow() 
    { 
     return PartialView("EditorRow", new MyOtherModelObject()); 
    } 
} 

ビュー(~/Views/Home/Index.cshtml):

@model MyModelObject 

@using(Html.BeginForm()) 
{ 
    @Html.HiddenFor(x => x.Id) 
    <div> 
     @Html.LabelFor(x => x.Name) 
     @Html.EditorFor(x => x.Name) 
    </div> 
    <div> 
     @Html.LabelFor(x => x.Description) 
     @Html.TextBoxFor(x => x.Description) 
    </div> 
    <hr/> 
    <div id="editorRows"> 
     @foreach (var item in Model.OtherModelObjects) 
     { 
      @Html.Partial("EditorRow", item); 
     } 
    </div> 
    @Html.ActionLink("Add another...", "BlankEditorRow", null, new { id = "addItem" }) 

    <input type="submit" value="Finished" /> 
} 

部分(~/Views/Home/EditorRow.cshtml):

@model MyOtherModelObject 

<div class="editorRow"> 
    @using (Html.BeginCollectionItem("OtherModelObjects")) 
    { 
     <div> 
      @Html.LabelFor(x => x.Name) 
      @Html.EditorFor(x => x.Name) 
     </div> 
     <div> 
      @Html.LabelFor(x => x.Description) 
      @Html.EditorFor(x => x.Description) 
     </div> 
     <a href="#" class="deleteRow">delete</a> 
    } 
</div> 

スクリプト:

$('#addItem').click(function() { 
    $.ajax({ 
     url: this.href, 
     cache: false, 
     success: function (html) { 
      $('#editorRows').append(html); 
     } 
    }); 
    return false; 
}); 

$('a.deleteRow').live('click', function() { 
    $(this).parents('div.editorRow:first').remove(); 
    return false; 
}); 

備考:BeginCollectionItemカスタムヘルパーは、私がリンクした同じ記事から取られているが、私は答えの完全性を期すためにここでそれを提供しています:

public static class HtmlPrefixScopeExtensions 
{ 
    private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_"; 

    public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName) 
    { 
     var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName); 
     string itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString(); 

     // autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync. 
     html.ViewContext.Writer.WriteLine(string.Format("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", collectionName, html.Encode(itemIndex))); 

     return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex)); 
    } 

    public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix) 
    { 
     return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix); 
    } 

    private static Queue<string> GetIdsToReuse(HttpContextBase httpContext, string collectionName) 
    { 
     // We need to use the same sequence of IDs following a server-side validation failure, 
     // otherwise the framework won't render the validation error messages next to each item. 
     string key = idsToReuseKey + collectionName; 
     var queue = (Queue<string>)httpContext.Items[key]; 
     if (queue == null) 
     { 
      httpContext.Items[key] = queue = new Queue<string>(); 
      var previouslyUsedIds = httpContext.Request[collectionName + ".index"]; 
      if (!string.IsNullOrEmpty(previouslyUsedIds)) 
       foreach (string previouslyUsedId in previouslyUsedIds.Split(',')) 
        queue.Enqueue(previouslyUsedId); 
     } 
     return queue; 
    } 

    private class HtmlFieldPrefixScope : IDisposable 
    { 
     private readonly TemplateInfo templateInfo; 
     private readonly string previousHtmlFieldPrefix; 

     public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix) 
     { 
      this.templateInfo = templateInfo; 

      previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix; 
      templateInfo.HtmlFieldPrefix = htmlFieldPrefix; 
     } 

     public void Dispose() 
     { 
      templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix; 
     } 
    } 
} 
+1

正確ではありません。オブジェクトの可変長リストの操作方法について説明していますが、別のオブジェクトの一部であるオブジェクトの可変長リストの操作方法については説明していません。 – DaveH

+0

@DaveHですが、これは実装が簡単で簡単です。あなたがしなければならないのは、編集しようとしているリストの 'IEnumerable 'プロパティを持つ新しいビューモデルを、あなたが' MyModelObject'を使って質問したのとまったく同じです。 –

+1

すごく簡単に言う?そして、これらすべてが親オブジェクトMyModelObjectにどのようにバインドされますか。 MVCのデフォルトのModelBindビヘイビアを書き直すことを含む、解決すべき解決策の大きな部分があります。これを行う際にセキュリティの問題がありますか?私はこのサンプルを使って作業していますが、単一のオブジェクトのリストを解決するための他のいくつかのフォーラム投稿で見つけましたが、私が効率的に解決したサンプルはあまり見かけませんでした。 – DaveH

0

私はこのブログポストhttp://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/で学んだレッスンを受け取り、ModelObjectにいくつかのプロパティがあり、その多くはリストである私のケースに適用することができました。

私はモデル内の複数のリストを扱うためにスクリプトを修正しました。できるだけ早く私のソリューションをブログにします。間違いなく私の現在のスプリントの後まで待たなければならない。私が終わったらリンクを掲示します。

関連する問題