具有动态集合的MVC模型绑定 [英] MVC Model Binding with Dynamic Collection

查看:63
本文介绍了具有动态集合的MVC模型绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据斯科特·汉塞尔曼(Scott Hanselman)开创性的文章,该文章关于:

According to the seminal Scott Hanselman article on the complexities of the ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries:

我们通过查找 parameterName[index].PropertyName
来读取属性 索引必须从零开始,并且要连续

We read in the properties by looking for parameterName[index].PropertyName
The index must be zero-based and unbroke

所以这个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" />

哪个会这样发布:

但是,如果我通过AJAX将新人加载到我的模型中,则会丢失将该人构建到模型中的上下文,并获得以下输出:

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:

<input type="text" name="FirstName" value="New" />

不会被模型联编程序接听.

Which won't get picked up by the model binder.

Q :在AJAX上动态添加新元素时,如何保留表达式树?

型号:/Model/Person.cs

Model: /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; }
}

控制器:Controllers/PersonController.cs

Controller: 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);
}

查看:Views/Person/Index.cshtml

View: 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>

查看:Views/Person/EditorTemplates/Person.cshtml

View: 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.

推荐答案

您可以安装 Html.BeginCollectionItem 实用工具,如下所示:

You can install the Html.BeginCollectionItem utility like this:

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>

这将生成一个GUID驱动的集合,如下所示:

Which will generate a GUID-driven collection like this:

<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:

这利用了 DefaultModelBinder ,它允许非顺序索引作为

This leverages the DefaultModelBinder which allows for Non-Sequential Indices as explained by Phil Haack:

好消息是,通过引入额外的隐藏输入,您可以允许任意索引.对于需要绑定到列表的每个项目,只需提供带有.Index后缀的隐藏输入即可.每个隐藏输入的名称都相同,这将为模型绑定器提供一个不错的索引集合,以便在绑定到列表时进行查找.

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. 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.

进一步阅读

  • A Partial View passing a collection using the Html.BeginCollectionItem helper
  • Submit same Partial View called multiple times data to controller?
  • Model Binding To A List - Phil Haack

这篇关于具有动态集合的MVC模型绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆