64

如果我希望表单中的一组输入绑定到ListMVC 4 中的 a,我知道以下input name属性命名约定将起作用:

<input name="[0].Id" type="text" />
<input name="[1].Id" type="text" />
<input name="[2].Id" type="text" />

但我很好奇模型活页夹的宽容度。例如,以下情况如何:

<input name="[0].Id" type="text" />
<input name="[3].Id" type="text" />
<input name="[8].Id" type="text" />

模型绑定器将如何处理这个问题?它会绑定到一个List长度为 9 的空值吗?或者它仍然会绑定到List长度为 3 的 a 吗?还是会完全窒息?

为什么我在乎

我想实现一个动态表单,用户可以在其中向表单添加行,也可以从表单中删除行。因此,如果我的用户从总共 8 行中删除了第 2 行,我想知道是否需要重新编号所有后续输入。

4

5 回答 5

50

有一种特定的线路格式可用于集合。这在 Scott Hanselman 的博客上进行了讨论:

http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx

Phil Haack 的另一篇博客文章在这里谈到了这一点:

http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

最后,一个完全符合您要求的博客条目:

http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/

于 2013-02-12T04:51:56.470 回答
18

我遵循了上面博客中链接的这种方法,并添加了一些可能对某些人有帮助的细节 - 特别是因为我想动态添加任意数量的行但不想使用 AJAX 这样做(我希望表单只提交在文中)。我也不想担心维护顺序 ID。我正在捕获开始和结束日期的列表:

查看型号:

public class WhenViewModel : BaseViewModel {
    public List<DateViewModel> Dates { get; set; }
    //... Other properties
}

开始/结束日期视图模型:

public class DateViewModel {
    public string DateID { get; set; }
    public DateTime? StartDate { get; set; }
    public DateTime? EndDate { get; set; }
}

然后在页面中使用它们(使用日期选择器):

<div class="grid-8-12 clear" id="DatesBlock">
@{
    foreach (DateViewModel d in Model.Dates) {
        @:<div class="grid-5-12 left clear">
            @Html.Hidden("Dates.Index", d.DateID)
            @Html.Hidden("Dates[" + d.DateID + "].DateID", d.DateID) //ID again to populate the view model
            @Html.TextBox("Dates[" + d.DateID + "].StartDate", 
                          d.StartDate.Value.ToString("yyyy-MM-dd"))
        @:</div>
        @:<div class="grid-5-12">
            @Html.TextBox("Dates[" + d.DateID + "].EndDate", 
                          d.EndDate.Value.ToString("yyyy-MM-dd"))
        @:</div>

        <script type="text/javascript">
            $('input[name="Dates[@d.DateID].StartDate"]')
               .datepicker({ dateFormat: 'yy-mm-dd'});
            $('input[name="Dates[@d.DateID].EndDate"]')
               .datepicker({dateFormat: 'yy-mm-dd'});
        </script>
     }
}
</div>
<a href="#" onclick="AddDatesRow()">Add Dates</a>

正如上面@ErikTheVikings 帖子中链接的博文所述,集合是由重复的隐藏元素创建的:@Html.Hidden("Dates.Index", d.DateID)对于页面上集合中的每个条目。

我想在不使用 AJAX 的情况下任意添加行以将数据发布回服务器,我通过创建一个隐藏的 div 来完成,该 div 包含集合中一个“行”/项目的模板:

隐藏的“模板”行:

<div id="RowTemplate" style="display: none">
    <div class="grid-5-12 clear">
        @Html.Hidden("Dates.Index", "REPLACE_ID")
        @Html.Hidden("Dates[REPLACE_ID].DateID", "REPLACE_ID") 
        @Html.TextBox("Dates[REPLACE_ID].StartDate", "")
    </div>
    <div class="grid-5-12">
        @Html.TextBox("Dates[REPLACE_ID].EndDate", "")
    </div>
</div>

然后使用 jQuery 克隆模板,提供一个随机 id 用于新行,并将现在可见的克隆行附加到上面的包含 div:

jQuery完成流程:

<script type="text/javascript">
    function AddDatesRow() {
        var tempIndex = Math.random().toString(36).substr(2, 5);
        var template = $('#RowTemplate');
        var insertRow = template.clone(false);
        insertRow.find('input').each(function(){ //Run replace on each input
            this.id = this.id.replace('REPLACE_ID', tempIndex);
            this.name = this.name.replace('REPLACE_ID', tempIndex);
            this.value = this.value.replace('REPLACE_ID', tempIndex);
        });
        insertRow.show();
        $('#DatesBlock').append(insertRow.contents());

        //Attach datepicker to new elements
        $('input[name="Dates['+tempIndex+'].StartDate"]')
            .datepicker({dateFormat: 'yy-mm-dd' });
        $('input[name="Dates['+tempIndex+'].EndDate"]')
            .datepicker({dateFormat: 'yy-mm-dd' });
    }
</script>

JSFiddle 结果示例:http: //jsfiddle.net/mdares/7JZh4/

于 2014-02-06T16:37:31.820 回答
3

我有看起来像这样的动态列表:

<ul id="okvedList" class="unstyled span8 editableList">
<li>
    <input data-val="true" data-val-required="The Guid field is required." id="Okveds_0__Guid" name="Okveds[0].Guid" type="hidden" value="2627d99a-1fcd-438e-8109-5705dd0ac7bb">
    --//--
</li>

所以当我添加或删除行(li 元素)时,我必须重新排序项目

    this.reorderItems = function () {
        var li = this.el_list.find('li');

        for (var i = 0; i < li.length; i++) {
            var inputs = $(li[i]).find('input');

            $.each(inputs, function () {
                var input = $(this);

                var name = input.attr('name');
                input.attr('name', name.replace(new RegExp("\\[.*\\]", 'gi'), '[' + i + ']'));

                var id = input.attr('id');
                input.attr('id', id.replace(new RegExp('_.*__', 'i'), '_' + i + '__'));
            });
        }
    };

此列表从客户端放入简单的 Html.BeginFrom 和服务器端的操作参数中的列表

于 2013-02-12T04:35:41.970 回答
1

我过去也遇到过类似的问题,我使用 KnockoutJS 来处理这种情况。

基本上,Knockout 以 JSON 字符串的形式发送集合,然后我在控制器中对它们进行反序列化。

欲了解更多信息:http ://learn.knockoutjs.com/#/?tutorial=collections

于 2013-02-12T04:43:24.827 回答
0

我遇到了一点问题,当我使用 Chrome 浏览器并单击后退按钮时,当 Chrome 浏览器未正确处理动态设置值时,我发现输入带有 type="hidden"。

也许我们可以改变

<input type="hidden" name="Detes.Index" value="2016/01/06" />

<div style="display: none">
    <input type="text" name="Detes.Index" value="2016/01/06" />
</div>

Form more info: Chrome doesn't cache hidden form field values for use in browser history http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/

于 2016-01-06T02:52:34.000 回答