我有一个视图模型,其中包含其他对象的集合。
public ParentViewModel { public int Id { get; set; } public string Name { get; set; } public List<ChildViewModel> Child { get; set; } } public ChildViewModel { public int Id { get; set; } public string FirstName { get; set; } }
在我的一种视图中,我传入ParentViewModel作为模型,然后使用
<%: Html.EditorFor(x => x) %>
其中显示Id和Name属性的表单。
当用户单击一个按钮时,我通过Ajax调用一个操作以加载带有Child集合的局部视图:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Child>>" %> <%: Html.EditorFor(x => x) %>
然后使用自定义模板Child来显示每个传入的Child的表单。
我遇到的问题是,Child自定义模板创建的表单未使用DefaultModelBinder使用的命名约定。
即字段名称是(当由Ajax加载时):
[0].FirstName
代替:
Child[0].FirstName
因此,在我的控制器中执行Edit操作:
[HttpPost] public virtual ActionResult Edit(int id, FormCollection formValues) { ParentViewModel parent = new ParentViewModel(); UpdateModel(parent); return View(parent); }
从提交的表单重新创建ParentViewModel无效。
我想知道通过Ajax完成在Custom Templates中加载然后能够使用UpdateModel的最佳方法是什么。
首先要注意的是,您需要记住默认的ModelBinder是递归的,它将尝试找出需要做的事情……非常聪明。要记住的另一件事是,您 不需要 使用html帮助器,实际的html也可以正常工作:-)
因此,首先使用模型,这里没有什么不同。
public class ParentViewModel { public int Id { get; set; } public string Name { get; set; } public List<ChildViewModel> Child { get; set; } } public class ChildViewModel { public int Id { get; set; } public string FirstName { get; set; } }
父局部视图 -这需要ParentViewModel的一个实例
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ParentViewModel>" %> <h2>Parent</h2> <%: Html.TextBox("parent.Name", Model.Name) %> <%: Html.Hidden("parent.Id", Model.Id) %> <% foreach (ChildViewModel childViewModel in Model.Child) { Html.RenderPartial("Child", childViewModel); } %>
子局部视图 -这需要ChildViewModel 的 单个 实例
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ChildViewModel>" %> <h3>Child</h3> <%: Html.Hidden("parent.Child.index", Model.Id) %> <%: Html.Hidden(string.Format("parent.Child[{0}].Id", Model.Id), Model.Id)%> <%: Html.TextBox(string.Format("parent.Child[{0}].FirstName", Model.Id), Model.FirstName) %>
此时要注意的一点是,索引值是用于计算列表中唯一记录的值。这不必是增量值。
那么,你怎么称呼它呢?在Index操作中,它将显示需要传递的数据。我已经设置了一些演示数据,并将其在ViewData字典中返回给索引视图。
所以控制器动作…
public ActionResult Index() { ViewData["Message"] = "Welcome to ASP.NET MVC!"; ViewData["Parent"] = GetData(); return View(); } private ParentViewModel GetData() { var result = new ParentViewModel { Id = 1, Name = "Parent name", Child = new List<ChildViewModel> { new ChildViewModel {Id = 2, FirstName = "first child"}, new ChildViewModel {Id = 3, FirstName = "second child"} } }; return result; }
在现实世界中,您会打电话给数据服务等。
最后是索引视图的内容:
<form action="<%: Url.Action("Edit") %>" method="post"> <% if (ViewData["Parent"] != null) { %> <% Html.RenderPartial("Parent", ViewData["Parent"]); %> <% } %> <input type="submit" /> </form>
保存
因此,现在我们已经显示了数据,我们如何将其恢复为动作?好的,这是默认模型联编程序将针对相对复杂形式的简单数据类型为您执行的操作。因此,您可以将要发布的操作的基本格式设置为:
[HttpPost] public ActionResult Edit(ParentViewModel parent) { }
这将为您提供具有原始ID(来自隐藏字段)的更新的详细信息,以便您可以根据需要进行更新/编辑。
新孩子通过阿贾克斯
您在通过ajax加载自定义模板中的问题中提到过,您的意思是如何为用户提供添加另一个孩子而无需回发的选项?
如果是这样,您可以执行以下操作…
添加操作 -需要一个操作,该操作将返回一个新的ChildViewModel
[HttpPost] public ActionResult Add() { var result = new ChildViewModel(); result.Id = 4; result.FirstName = "** to update **"; return View("Child", result); }
为了方便演示,我给了它一个id。
然后,您需要一种调用代码的方法,因此唯一需要更新的视图是主索引视图。这将包括用于获取操作结果的javascript,用于调用代码的链接以及要附加到html的目标HTML标记。另外,不要忘记在母版页或视图顶部添加对jQuery的引用。
索引视图-已更新!
<script type="text/javascript"> function add() { $.ajax( { type: "POST", url: "<%: Url.Action("Add", "Home") %>", success: function(result) { $('#newchild').after(result); }, error: function(req, status, error) { } }); } </script> <form action="<%: Url.Action("Edit") %>" method="post"> <% if (ViewData["Parent"] != null) { %> <% Html.RenderPartial("Parent", ViewData["Parent"]); %> <% } %> <div id="newchild"></div> <br /><br /> <input type="submit" /> <a href="#" onclick="JavaScript:return add();">add child</a> </form>
这将调用添加操作,并在返回到提交按钮上方的newChild div时追加响应。
我希望这篇长文章有用。
请享用 :-)