MVC 4列表模型绑定如何工作?
如果我想要一个表单中的一组input绑定到MVC 4中的List
,我知道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" />
模型活页夹如何处理? 它会绑定到一个长度为9的List
,用空值吗? 或者它还会绑定到长度为3的List
? 或者它会完全窒息?
为什么我在乎
我想实现一个dynamic的表单,用户可以在表单中添加行,也可以从表单中删除行。 所以,如果我用户从8行总共删除第2行,我想知道是否需要重新编号所有后续input。
有一个特定的线路格式用于集合。 Scott Hanselman的博客在这里讨论这个问题:
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/
我遵循上面的博客链接这种方法,并添加了一些细节,可能会有所帮助一些 – 特别是因为我想dynamic添加任意数量的行,但不想使用AJAX这样做(我希望窗体只提交在post中)。 我也不想担心维护序列号。 我正在捕获开始和结束date的列表:
查看模型:
public class WhenViewModel : BaseViewModel { public List<DateViewModel> Dates { get; set; } //... Other properties }
开始/结束date查看模式:
public class DateViewModel { public string DateID { get; set; } public DateTime? StartDate { get; set; } public DateTime? EndDate { get; set; } }
然后在页面中使用它们(使用datepicker):
<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>
正如上面描述的@ErikTheVikingspost中链接的博客文章所描述的那样,集合由页面集合中每个条目的重复隐藏元素创build: @Html.Hidden("Dates.Index", d.DateID)
。
我想任意添加行而不使用AJAX将数据发回到服务器,我通过创build一个隐藏的div包含集合中的一个“row”/ item的模板:
隐藏“模板”行:
<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/
我有这样的dynamic列表:
<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>
所以当我添加或删除行(李元素)我不得不重新sorting项目
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中,并且像服务器侧的List in action参数一样
我以前也遇到类似的问题,我用KnockoutJS来处理这种情况。
基本上,Knockout以JSONstring发送集合,然后在我的控制器中对它们进行反序列化。
欲了解更多信息: http : //learn.knockoutjs.com/#/?tutorial=collections
当我使用Chrome浏览器并单击后退button时,我遇到了一些问题,当Chrome浏览器未正确处理dynamic设置的值时,我发现input的types为“隐藏”。
也许我们可以改变
<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>
形成更多信息: Chrome浏览器不caching浏览器历史logging中使用的隐藏表单字段值 http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/