MVC数组模型绑定
ASP.NET MVC数组模型绑定
在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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
<div style="width:680px"> <div class="form-group"> <label>航班</label><br/> @Html.TextBoxFor(p => p.FlightNo, new { placeholder = "航班号" }) </div> <div class="form-group"> <label>乘机人</label> <table class="passenger" > <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:;" onclick="removePassenger(this)" >删除</a> </td> </tr> } } </tbody> </table> <div style="margin-top:10px"> <a href="javascript:;" onclick="addPassenger()">添加乘机人</a> </div> </div></div> |
由于ASP.NET MVC的模型绑定器(DefaultModelBinder)具备自动解析形如"[0].属性名"、"[1].属性名"的能力,所以可以在模板文件中以占位符的形式来表示数组下标:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
<!-- 乘机人模板 --><script type="text/html" id="passengerTemplate"> <tr> <td>姓名:</td> <td><input id="Passengers_{}__Name" name="Passengers[{}].Name" type="text" value=""></td> <td>身份证号:</td> <td><input id="Passengers_{}__IdNo" name="Passengers[{}].IdNo" type="text" value=""></td> <td> <a href="javascript:;" onclick="removePassenger(this)">删除</a> </td> </tr></script> |
以上代码中的"{}"是数组下标占位符。当添加乘机人时,可预先计算已有乘机人个数,然后再使用JavaScript替换”{}“为数组下标。
|
1
2
3
4
5
6
7
8
|
// 添加乘机人function addPassenger() { // 当前添加行数组元素下标 var index = $(".passenger").find("tbody").find("tr").length; //{}是数组元素下标占位符 var passengerHTML = $('#passengerTemplate').html().replace(/{}/g, index); $(".passenger").find("tbody").append(passengerHTML);} |
当删除乘机人时,注意如果删除的不是最后一个,会发生索引断裂问题,需要重新调整数组下标:
|
1
2
3
4
5
6
7
8
9
10
11
|
// 删除乘机人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,非常轻量化。
这种方式的基本原理是前端使用Vue.js声明视图模型并进行绑定,然后提交表单时把模型序列化为json字符串传递到后台,后台再使用Json.net反序列化为C#对象。
由于Vue.js的绑定特点,我们只需要操作数组元素即可,不需要额外关注DOM操作,节省了不少工作量。
首先,需要声明视图模型,并使用Vue.js进行绑定:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
<script src="~/Scripts/vue.js"></script><script type="text/javascript"> // 视图模型 var viewModel = { FlightNo: '', Passengers: [ { ElementId: 'passenger_1', Name: '', IdNo: '' } ] } // 模型绑定 new Vue({ el: '#app', data: viewModel, methods: { removePassenger: function (elementId) { for (var i = 0; i < viewModel.Passengers.length; i++) { if (viewModel.Passengers[i].ElementId == elementId) { viewModel.Passengers.splice(i, 1); } } }, addPassenger: function () { var tb = document.getElementsByTagName('table')[0]; var index = tb.rows[tb.rows.length - 1].getElementsByTagName('input')[0].getAttribute("id").split('_')[1]; viewModel.Passengers.push({ Name: '', IdNo: '', ElementId: 'passenger_' + (index + 1) }); }, submitForm: function () { var jsonString = JSON.stringify(viewModel); document.getElementById("viewModel").value = jsonString; return true; } } });</script> |
然后,在视图中使用Vue.js绑定:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
<form action="/Order2/NewPost" method="post"> <div id="app" style="width:680px"> <div class="form-group"> <label>航班</label><br /> <input v-model="FlightNo" type="text" placeholder="航班号" /> </div> <div class="form-group"> <label>乘机人</label> <table class="passenger"> <tbody> <tr v-for="passenger in Passengers"> <td>姓名:</td> <td><input v-model="passenger.Name" v-bind:id="passenger.ElementId" type="text" /></td> <td>身份证号:</td> <td><input v-model="passenger.IdNo" type="text" /></td> <td> <a href="javascript:;" v-on:click="removePassenger(passenger.ElementId)">删除</a> </td> </tr> </tbody> </table> <div style="margin-top:10px"> <a href="javascript:;" v-on:click="addPassenger">添加乘机人</a> </div> <div style="margin-top:10px"> <input type="submit" class="btn btn-default" v-on:click="submitForm" /> </div> </div> </div> <input type="hidden" id="viewModel" name="viewModel" /></form> |
在Controller里,我们反序列化即可得到对应的C#强类型模型:
|
1
2
3
4
5
6
7
8
9
10
|
[HttpPost]public ActionResult NewPost(){ var jsonString = Request.Form["viewModel"]; Models.OrderModel model = Newtonsoft.Json.JsonConvert.DeserializeObject<Models.OrderModel>(jsonString); if (model != null) { // our code here... } return RedirectToAction("Index", "Home");} |
这两种方式均可以实现动态数组绑定,方式一使用js进行占位符替换,表单中的元素都以[index].属性名的方式命名,然后由MVC默认的模型绑定器来转化模型;
方式二使用Vue.js来直接进行模型绑定,提交表单时将模型序列化为json字符串,然后后端再反序列化,最终得到强类型模型。
一个简单的示例程序(12e5)
MVC数组模型绑定的更多相关文章
- ASP.NET MVC数组模型绑定
在ASP.NET MVC中使用Razor语法可以在视图中方便地展示数组,如果要进行数组模型绑定,会遇到索引断裂问题,如下示例: <input type="text" name ...
- 白话学习MVC(六)模型绑定
一.什么是模型绑定? 模型绑定存在的意义就是为Action的参数提供值,例如:如下表单中提交了数据,那么Action(即:Index)的参数Id,Name的值就是表单中对应的name属性相同的值,而表 ...
- .net的WebForm模拟MVC进行模型绑定,让自己少操劳
一.前言 用过MVC的兄弟们都知道,MVC有模型绑定表单提交的数据功能,那么我也想偷个懒也写个WebForm版的模型绑定.这里主要定义一个泛型方法,然后通过反射把表单上对应属性名字的值赋值到反射创建类 ...
- asp.net MVC 自定义模型绑定 从客户端中检测到有潜在危险的 Request.QueryString 值
asp.net mvc 自定义模型绑定 有潜在的Requset.Form 自定义了一个模型绑定器.前端会传过来一些敏感字符.调用bindContext. valueProvider.GetValue( ...
- ASP.NET Core MVC/WebAPi 模型绑定探索
前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...
- MVC复杂模型绑定
当初遇到业务需求ajax提交一组对象数组到服务器.但是苦于mvc的默认绑定器.绑定不上去.好吧只有靠自己了. 当初就是参考这个大大的博客:http://www.cnblogs.com/xfrog/ar ...
- asp.net mvc 自定义模型绑定
在asp.net mvc的控制器中如果能够活用模型的自动绑定功能的话能够减少许多工作量.但是如果我们想要对前台传来的数据进行一些处理再绑定到模型上,该怎么做呢? 这里用一个绑定用户数据的小案例来讲解a ...
- MVC之模型绑定
1.前言 MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显示分离的方 ...
- [MVC]自定义模型绑定器,从表单对模型进行赋值
一.奇葩的问题 之前自己造轮子的时候,遇到一个很奇怪的问题,虽然需求很奇葩,但是还是尝试解决了一下 当提交的表单里包含多个重复名称的字段的时候,例如 <form action="/Te ...
随机推荐
- iOS 获取当前时间以及计算年龄(时间差)
获取当前时间 NSDate *now = [NSDate date]; NSLog(@"now date is: %@", now); NSCalendar *calendar = ...
- Stack栈的三种含义
理解stack栈对于理解程序的执行至关重要.easy混淆的是,这个词事实上有三种含义,适用于不同的场合,必须加以区分. 含义一:数据结构 stack的第一种含义是一组数据的存放方式,特点为LIFO,即 ...
- SharePoint数据视图无法打开
最近在折腾SharePoint,之前列表常用的“数据视图”居然不能打开,提示“没有安装Sharepoit foundation 数据兼容组件”如图: 上网G下.度下有说要删除注册表.要安装office ...
- QT在Windows控制台下输出
原地址:http://blog.csdn.net/fjb2080/article/details/9013047 在windows的控制台下输出,需要在pro文件中加入: CONFIG += cons ...
- ubuntu 安装maven提示出错 The program 'mvn' can be found in the following packages
问题: I am trying to install apache maven 3 in Ubuntu 12.04 lts. What I did was open the terminal then ...
- [置顶] 关于本博客 http://www.imobilebbs.com
由于时间上的关系,本博客不再和引路蜂移动软件博客同步更新, 请直接访问 http://www.imobilebbs.com 谢谢您的支持,再见 引路蜂博客
- 【Android】 -- 使用UncaughtExceptionHandler捕捉全局异常
在综合统计SDK(欧盟统计局的朋友,百度统计)之后.有一个非常有利的功能测试:错误分析.此功能可以在程序的执行中遇到崩溃(runtimeException)反馈给server,帮助开发者提高产品.多功 ...
- Android的StrictMode
转自:http://blog.csdn.net/tonyfield/article/details/8238251 Android 2.3提供一个称为严苛模式(StrictMode)的调试特性,Goo ...
- Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO
距离上次发布(android高仿系列)今日头条 --新闻阅读器 (二) 相关的内容已经半个月了,最近利用空闲时间,把今日头条客户端完善了下.完善的功能一个一个全部实现后,就放整个源码.开发的进度就是按 ...
- Shell 输入/输出重定向
大多数 UNIX 系统命令从你的终端接受输入并将所产生的输出发送回到您的终端.命令通常从标准输入的地方读取输入,默认是终端.标准输出,默认情况下是终端. 重定向命令列表如下: 命令 说明 comm ...