本文转载自 ASP.NET MVC数组模型绑定 ,https://www.cnblogs.com/choon/p/5429065.html
内容根据评论内容中的方式有所调整
在ASP.NET MVC中使用Razor语法可以在视图中方便地展示数组,如果要进行数组模型绑定,会遇到索引断裂问题,如下示例:
<input type="text" name="[0].Name" />
<input type="text" name="[1].Name" />
<input type="text" name="[2].Name" />
<input type="text" name="[4].Name" />
<input type="text" name="[5].Name" />
数组Name在索引3处断裂,在模型绑定器解析完成后,会丢弃后面的4和5,只有0、1、2会被正确解析到对应模型中。
这种断裂在进行动态数组绑定时会经常发生。
下面,以一个案例来探讨如何进行动态数组绑定。假设有以下应用场景:
要求能够动态地添加和删除乘机人,最终提交表单后乘机人信息要填充到视图模型中的一个数组或集合属性中,以方便我们进行后续业务处理。
方式一:使用占位符替换
第一种方式我称之为”占位符替换“,使用的是ASP.NET MVC默认的模型绑定器(DefaultModelBinder)并结合前端处理。
首先,第一步,根据业务场景设计视图模型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public class OrderModel { /// <summary> /// 航班号 /// </summary> public string FlightNo { get ; set ; } /// <summary> /// 乘机人 /// </summary> public List<Passenger> Passengers { get ; set ; } } public class Passenger { public string Name { get ; set ; } public string IdNo { get ; set ; } } |
其次,将此视图模型传递给视图:
1
2
3
4
5
6
7
8
|
public ActionResult New() { Models.OrderModel orderModel = new Models.OrderModel(); List<Models.Passenger> passenger = new List<Models.Passenger>(); passenger.Add( new Models.Passenger()); orderModel.Passengers = passenger; return View(orderModel); } |
再在视图文件中进行展示:
1 <div style="680px"> 2 <form method="post" id="postForm"> 3 <div class="form-group"> 4 <label>航班</label><br /> 5 @Html.TextBoxFor(p => p.FlightNo, new { placeholder = "航班号" }) 6 </div> 7 <div class="form-group"> 8 <label>乘机人</label> 9 <table class="passenger"> 10 <tbody> 11 @if (Model.Passengers != null && Model.Passengers.Count > 0) 12 { 13 for (int i = 0; i < Model.Passengers.Count; i++) 14 { 15 <tr> 16 <td>姓名:</td> 17 <td>@Html.TextBoxFor(p => Model.Passengers[i].Name) <input name="Passengers.Index" type="hidden" value="@i" /></td> 18 <td>身份证号:</td> 19 <td>@Html.TextBoxFor(p => Model.Passengers[i].IdNo)</td> 20 <td> 21 <a href="javascript:;" onclick="removePassenger(this)">删除</a> 22 </td> 23 </tr> 24 } 25 } 26 </tbody> 27 </table> 28 <div style="margin-top:10px"> 29 <a href="javascript:;" onclick="addPassenger()">添加乘机人</a> 30 </div> 31 </div> 32 @*<button type="submit">提交</button>*@ 33 <button type="button" onclick="myfunction()">提交</button> 34 </form> 35 </div>
由于ASP.NET MVC的模型绑定器(DefaultModelBinder)具备自动解析形如"[0].属性名"、"[1].属性名"的能力,所以可以在模板文件中以占位符的形式来表示数组下标:
1 <!-- 乘机人模板 --> 2 <script type="text/html" id="passengerTemplate"> 3 <tr> 4 <td>姓名:</td> 5 <td><input name="Passengers.Index" type="hidden" value="{}" /><input id="Passengers_{}__Name" name="Passengers[{}].Name" type="text" value=""></td> 6 <td>身份证号:</td> 7 <td><input id="Passengers_{}__IdNo" name="Passengers[{}].IdNo" type="text" value=""></td> 8 <td> 9 <a href="javascript:;" onclick="removePassenger(this)">删除</a> 10 </td> 11 </tr> 12 </script>
以上代码中的"{}"是数组下标占位符。当添加乘机人时,可预先随机生成一个key,然后再使用JavaScript替换”{}“为数组下标。
// 添加乘机人
当删除乘机人时,因为存在隐藏域,不需要考虑索引断裂问题:
// 删除乘机人
function
removePassenger(e) {
$(e).parents("tr").remove();}
这样,当我们提交表单时,乘机人信息就会自动填充到模型的Passengers属性中。
方式二:重写DefaultModelBinder
从Request.Form表单里面读取所有不同的索引,拼接索引读取数据。
1 public class TestModelBinder : DefaultModelBinder, IModelBinder 2 { 3 public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 4 { 5 var model = base.BindModel(controllerContext, bindingContext); 6 var inspectKeys = controllerContext.HttpContext.Request.Form.AllKeys.Where(c => c.Contains("IdNo")).ToList(); 7 List<Passenger> passengerList = new List<Passenger>(); 8 List<string> inspectLengths = new List<string>(); 9 for (int i = 0; i < inspectKeys.Count; i++) 10 { 11 string result = System.Text.RegularExpressions.Regex.Replace(inspectKeys[i], @"[^0-9]+", ""); 12 if (!inspectLengths.Contains(result) && !string.IsNullOrWhiteSpace(result)) 13 { 14 inspectLengths.Add(result); 15 } 16 } 17 for (int i = 0; i < inspectLengths.Count; i++) 18 { 19 Passenger dto = new Passenger() 20 { 21 IdNo = controllerContext.HttpContext.Request.Form["IdNo" + inspectLengths[i]], 22 Name = controllerContext.HttpContext.Request.Form["Name" + inspectLengths[i]], 23 }; 24 passengerList.Add(dto); 25 } 26 var entity = model as OrderModel; 27 if (entity != null) 28 { 29 entity.Passengers = passengerList; 30 return entity; 31 } 32 return model; 33 } 34 }
控制器接收代码:
1 [HttpPost] 2 public ActionResult Test(Models.OrderModel orderModel) 3 { 4 return View(orderModel); 5 }
文件下载:TestCode.zip