ASP.NET MVC数组模型绑定详解

在ASP.NET MVC中使用Razor语法可以在视图中方便地展示数组,如果要进行数组模型绑定,会遇到索引断裂问题,如下示例:

<input type="text" /> <input type="text" /> <input type="text" /> <input type="text" /> <input type="text" />

数组Name在索引3处断裂,在模型绑定器解析完成后,会丢弃后面的4和5,只有0、1、2会被正确解析到对应模型中。

这种断裂在进行动态数组绑定时会经常发生。

下面,以一个案例来探讨如何进行动态数组绑定。假设有以下应用场景:

ASP.NET MVC数组模型绑定详解

要求能够动态地添加和删除乘机人,最终提交表单后乘机人信息要填充到视图模型中的一个数组或集合属性中,以方便我们进行后续业务处理。

方式一:使用占位符替换
第一种方式我称之为”占位符替换“,使用的是ASP.NET MVC默认的模型绑定器(DefaultModelBinder)并结合前端处理。

首先,第一步,根据业务场景设计视图模型:

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; } }

其次,将此视图模型传递给视图:

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

再在视图文件中进行展示:

<div> <div> <label>航班</label><br/> @Html.TextBoxFor(p => p.FlightNo, new { placeholder = "航班号" }) </div> <div> <label>乘机人</label> <table > <tbody> @if (Model.Passengers != null && Model.Passengers.Count > 0) { for(int i = 0; i < Model.Passengers.Count; i++) { <tr> <td>姓名:</td> <td>@Html.TextBoxFor(p => Model.Passengers[i].Name)</td> <td>身份证号:</td> <td>@Html.TextBoxFor(p => Model.Passengers[i].IdNo)</td> <td> <a href="javascript:;" >删除</a> </td> </tr> } } </tbody> </table> <div> <a href="javascript:;">添加乘机人</a> </div> </div> </div>

由于ASP.NET MVC的模型绑定器(DefaultModelBinder)具备自动解析形如"[0].属性名"、"[1].属性名"的能力,所以可以在模板文件中以占位符的形式来表示数组下标:

<!-- 乘机人模板 --> <script type="text/html"> <tr> <td>姓名:</td> <td><input type="text" value=""></td> <td>身份证号:</td> <td><input type="text" value=""></td> <td> <a href="javascript:;">删除</a> </td> </tr> </script>

以上代码中的"{}"是数组下标占位符。当添加乘机人时,可预先计算已有乘机人个数,然后再使用JavaScript替换”{}“为数组下标。

// 添加乘机人 function addPassenger() { // 当前添加行数组元素下标 var index = $(".passenger").find("tbody").find("tr").length; //{}是数组元素下标占位符 var passengerHTML = $('#passengerTemplate').html().replace(/{}/g, index); $(".passenger").find("tbody").append(passengerHTML); }

当删除乘机人时,注意如果删除的不是最后一个,会发生索引断裂问题,需要重新调整数组下标:

// 删除乘机人 function removePassenger(e) { $(e).parents("tr").remove(); // 依次遍历表格的每行,重新调整数组下标 var tb = $(".passenger").first(); var count = tb.find("tbody").find("tr").length; for (var i = 0; i < count; i++) { var newTR = tb.find("tr").eq(i).formhtml().replace(/\[\d+\]/g, '[' + i + ']');//重新调整数组元素下标 tb.find("tr").eq(i).html(newTR); } }

这样,当我们提交表单时,乘机人信息就会自动填充到模型的Passengers属性中。

方式二:使用Vue.js
使用第一种方式需要编写大量前端代码,包括模板文件,添加删除事件,还需要处理重新调整顺序时的插值问题。

如果使用前端MVVM框架会让这一流程变得简单,目前比较流行的前端MVVM框架有AngularJS,有老古董KnockoutJS,也有新兴小众框架Vue.js。

AngularJS比较庞大,这么简单的一个模型绑定用Anuglar有一种杀鸡用牛刀的感觉;Knockout和Vue都是轻量级的MVVM框架,但Knockout需要包裹原生数据来制造可观察对象,取值和赋值时需要采用函数调用的形式,使用起来不是很方便,所以我选择了Vue.js。Vue.js是一个轻量高效的库,它没有像Angular的module、controller、scope、factory、service这种API,核心就是一个模型绑定功能。大小只有70kb,gzip压缩后只有25kb,非常轻量化。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wjwzpf.html