[英]MVC Model Binding with Dynamic Collection
According to the seminal Scott Hanselman article on the complexities of the ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries : 根据具有开创性的Scott Hanselman文章,关于将模型绑定到数组,列表,集合,字典的ASP.NET Wire格式的复杂性:
We read in the properties by looking for
parameterName[index].PropertyName
我们通过查找parameterName[index].PropertyName
读取属性。
The index must be zero-based and unbroke 索引必须从零开始,并且是连续的
So this HTML: 所以这个HTML:
<input type="text" name="People[0].FirstName" value="George" />
<input type="text" name="People[1].FirstName" value="Abraham" />
<input type="text" name="People[2].FirstName" value="Thomas" />
Which will post like this: 它将像这样发布:
However, if I load a new person into my model over AJAX, I lose the context for building that person into the model and get the following output: 但是,如果我通过AJAX将新人员加载到模型中,则会丢失将该人员构建到模型中的上下文,并获得以下输出:
<input type="text" name="FirstName" value="New" />
Which won't get picked up by the model binder. 模型绑定器不会将其拾取。
Q : How can I preserve the expression tree when dynamically adding new elements over AJAX? 问 : 在AJAX上动态添加新元素时,如何保留表达式树?
Model: /Model/Person.cs
型号: /Model/Person.cs
public class PersonViewModel
{
public List<Person> People { get; set; }
}
public class Person
{
public String FirstName { get; set; }
public String LastName { get; set; }
}
Controller: Controllers/PersonController.cs
控制器: Controllers/PersonController.cs
[HttpGet]
public ActionResult Index()
{
List<Person> people = new List<Person> {
new Person { FirstName = "George" , LastName = "Washington"},
new Person { FirstName = "Abraham" , LastName = "Lincoln"},
new Person { FirstName = "Thomas" , LastName = "Jefferson"},
};
PersonViewModel model = new PersonViewModel() {People = people};
return View(model);
}
[HttpPost]
public ActionResult Index(PersonViewModel model)
{
return View(model);
}
public ActionResult AddPerson(String first, String last)
{
Person newPerson = new Person { FirstName = first, LastName = last };
return PartialView("~/Views/Person/EditorTemplates/Person.cshtml", newPerson);
}
View: Views/Person/Index.cshtml
查看: Views/Person/Index.cshtml
@model PersonViewModel
@using (Html.BeginForm()) {
<table id="table">
<thead>
<tr>
<th>@Html.DisplayNameFor(model => model.People.First().FirstName)</th>
<th>@Html.DisplayNameFor(model => model.People.First().LastName)</th>
</tr>
</thead>
<tbody>
@for (int i = 0; i < Model.People.Count; i++)
{
@Html.EditorFor(model => model.People[i])
}
</tbody>
</table>
<input type="button" value="Add Person" id="add"/>
<input type="submit" value="Save" />
}
<script type="text/javascript">
$("#add").click(function() {
var url = "@Url.Action("AddPerson")?" + $.param({ first: "", last: "" });
$.ajax({
type: "GET",
url: url,
success: function(data) {
$("#table tbody").append(data);
}
});
});
</script>
View: Views/Person/EditorTemplates/Person.cshtml
查看: Views/Person/EditorTemplates/Person.cshtml
@model Person
<tr>
<td>@Html.EditorFor(model => model.FirstName)</td>
<td>@Html.EditorFor(model => model.LastName)</td>
</tr>
NOTE : There are other complexities when deleting an item that I'm not looking to address here per se. 注意 :删除我本身不打算解决的项目时,还有其他复杂性。 I'd just like to add an element and know that it belongs in a nested context alongside other properties. 我只想添加一个元素,并知道它与其他属性一起属于嵌套上下文。
You can install the Html.BeginCollectionItem
utility like this: 您可以像这样安装 Html.BeginCollectionItem
实用程序:
PM> Install-Package BeginCollectionItem
Then wrap your collection item partial view like this: 然后像这样包装您的收藏品局部视图:
@model Person
<tr>
@using (Html.BeginCollectionItem("people"))
{
<td>@Html.EditorFor(model => model.FirstName)</td>
<td>@Html.EditorFor(model => model.LastName)</td>
}
</tr>
Which will generate a GUID-driven collection like this: 它将生成一个GUID驱动的集合,如下所示:
<tr>
<input type="hidden" name="people.index" autocomplete="off"
value="132bfe2c-75e2-4f17-b54b-07e011971d78">
<td><input class="text-box single-line" type="text" value="Abraham"
id="people_132bfe2c-75e2-4f17-b54b-07e011971d78__FirstName"
name="people[132bfe2c-75e2-4f17-b54b-07e011971d78].FirstName"></td>
<td><input class="text-box single-line" type="text" value="Lincoln"
id="people_132bfe2c-75e2-4f17-b54b-07e011971d78__LastName"
name="people[132bfe2c-75e2-4f17-b54b-07e011971d78].LastName"></td>
</tr>
Now we get posted form data that look like this: 现在,我们获得了如下所示的表单数据:
This leverages the DefaultModelBinder which allows for Non-Sequential Indices as explained by Phil Haack : 这利用了DefaultModelBinder ,它允许非连续索引,如Phil Haack所解释 :
The good news is that by introducing an extra hidden input, you can allow for arbitrary indices. 好消息是,通过引入额外的隐藏输入,您可以允许任意索引。 Just provide a hidden input with the
.Index
suffix for each item we need to bind to the list. 只需为需要绑定到列表的每个项目提供一个带有.Index
后缀的隐藏输入。 The name of each of these hidden inputs is the same, which will give the model binder a nice collection of indices to look for when binding to the list. 这些隐藏输入的每个名称都是相同的,这将为模型绑定器提供一个不错的索引集合,以便在绑定到列表时查找。
Right away, your model should build just fine, but you'll also be able to add and remove items as well. 立即,您的模型应该构建良好,但是您也可以添加和删除项目。
Further Reading 进一步阅读
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.