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)并结合前端处理。

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

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 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].属性名"的能力,所以可以在模板文件中以占位符的形式来表示数组下标:

<!-- 乘机人模板 -->
<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替换”{}“为数组下标。

// 添加乘机人
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,非常轻量化。

这种方式的基本原理是前端使用Vue.js声明视图模型并进行绑定,然后提交表单时把模型序列化为json字符串传递到后台,后台再使用Json.net反序列化为C#对象。

由于Vue.js的绑定特点,我们只需要操作数组元素即可,不需要额外关注DOM操作,节省了不少工作量。

首先,需要声明视图模型,并使用Vue.js进行绑定:

<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绑定:

<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#强类型模型:

[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字符串,然后后端再反序列化,最终得到强类型模型。

以上就是本文的全部内容,希望对大家的学习有所帮助。

(0)

相关推荐

  • asp.net中ListBox 绑定多个选项为选中及删除实现方法

    我们先来看listbox绑定多选项实现 复制代码 代码如下: <%@ Page Language="C#" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"

  • asp.net中将数据库绑定到DataList控件的实现方法与实例代码

    解决方法1: datalist databind() 解决方法2: 查看MSDN上的详细说明资料 解决方法3: 在DataList的模板中用table表格,如: 复制代码 代码如下: <asp:DataList ID="dlDetailedInfo" runat="server" OnItemDataBound="dlDetailedInfo_ItemDataBound" Width="100%"> <Ite

  • ASP.NET中ListView(列表视图)的使用前台绑定附源码

    1.A,运行效果图 1.B,源代码 复制代码 代码如下: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="DropLvw.aspx.cs" Inherits="DropLvw" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "htt

  • asp.net读取excel中的数据并绑定在gridview

    前台label,DropDownList,gridview控件 aspx.cs核心代码: 复制代码 代码如下: using System.Data.OleDb;//需要引入命名 public void Excel_Click(object sender, EventArgs e) { if (this.AttachmentFile.Value == "" && this.Label1.Text == "" && DropDownLis

  • asp.net数据绑定DataBind使用方法

    简单介绍 DataBindDataBind包括三大方法,Repeater,DataList和DataGrid,这些控件都位于 System.Web.UI.WebControls 命名空间中,从 WebControl 基类中直接或间接派生出来的.这些方法都是通过HTML来显示数据的内容.建立DataBind所有的DataBind都应该用DataBind() 函数来建立(注意如果你用的是C#,请注意大小写)数据绑定,是整个页面PAGE和所有控件的一个方法,也就是说,他可以被所有的控件使用,你建立数据

  • asp.net ListView 数据绑定

    代码如下: public partial class Form1 : Form { public Form1() { InitializeComponent(); string strsql = @"server=.;uid=sa;pwd=sa;database=Northwind"; SqlConnection my_Conn = new SqlConnection(strsql); my_Conn.Open(); string str_sql ="select * fro

  • Asp.net中的数据绑定Eval和Bind应用示例

    关键字: Eval (单项绑定)单项的数据绑定一般用于数据展示.Eval数据绑定应用的反射原理来返回数据. Bind (双向绑定)双向的数据绑定除了展示数据,还要将界面数据的变动自动写回到绑定的数据源中. Eval 单向数据绑定,将数据源中的数据展现到界面上.当我们提及数据展现时,必然会考虑到数据的格式问题,例如日期格式.当然,这也是Eval关心的内容. 一般数据绑定 <%#Eval("属性名称")%> 带数据格式的数据绑定 <%#Eval("属性名称&qu

  • asp.net gridview代码绑定

    拖一个gridview控件上去,然后在Page_Load里编写代码,当然我是手动用代码绑定的,这里使用了DataAdapter,它的构造函数接受了一个sql字符串和Connection对象,用它可以不必打开和关闭Connection对象,DataAdapter会自行处理,还用到了DateSet,这里新建了一个空的DateSet对象,并调用DataAdapter的Fill方法填充数据,然后通过设定GridView的数据源,调用它的DataBind方法就实现了数据绑定. 程序代码 复制代码 代码如下

  • asp.net中绑定TextBox回车事件的解决方法

    1.将页面上的回车事件都绑定到按钮上 复制代码 代码如下: function EnterTextBox(e) { var msie = (document.all) ? true : false; var keycode; if(!msie) keycode = window.event ? e.keyCode : e.which; else keycode = e.keyCode; //alert(keycode); if(keycode==13 && document.getEleme

  • 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" />

  • JWT + ASP.NET MVC时间戳防止重放攻击详解

    时间戳作用 客户端在向服务端接口进行请求,如果请求信息进行了加密处理,被第三方截取到请求包,可以使用该请求包进行重复请求操作.如果服务端不进行防重放攻击,就会服务器压力增大,而使用时间戳的方式可以解决这一问题. 上一篇讲到JWT安全验证操作,现在结合时间戳进行防重复攻击和被第三方抓包工具截取到Headers中token,进行模拟请求操作. 防篡改 一般使用的方式就是把参数拼接,当前项目AppKey,双方约定的"密钥",加入到Dictionary字典集中,按ABCD顺序进行排序,最后在M

  • Java Spring MVC获取请求数据详解操作

    目录 1. 获得请求参数 2. 获得基本类型参数 3. 获得POJO类型参数 4. 获得数组类型参数 5. 获得集合类型参数 6. 请求数据乱码问题 7. 参数绑定注解 @requestParam 8. 获得Restful风格的参数 9. 自定义类型转换器 1.定义转换器类实现Converter接口 2.在配置文件中声明转换器 3.在<annotation-driven>中引用转换器 10. 获得Servlet相关API 11. 获得请求头 11.1 @RequestHeader 11.2 @

  • C#利用ASP.NET Core开发学生管理系统详解

    目录 涉及知识点 创建项目 登录模块 1. 创建控制器--LoginController 2. 创建登录视图 3. 创建用户模型 4. 创建数据库操作DataContext 5. 创建数据库和表并构造数据 6. 添加数据库连接配置 7. 添加注入信息 8. 运行测试 随着技术的进步,跨平台开发已经成为了标配,在此大背景下,ASP.NET Core也应运而生.本文主要利用ASP.NET Core开发一个学生管理系统为例,简述ASP.NET Core开发的常见知识点,仅供学习分享使用,如有不足之处,

  • 20行代码简单实现koa洋葱圈模型示例详解

    目录 引言 koa中间件的使用 洋葱圈模型 洋葱圈模型的实现,koa-compose 单次调用限制 koa-compose与流程引擎 总结 引言 koa想必很多人直接或间接的都用过,其源码不知道阅读本文的你有没有看过,相当精炼,本文想具体说说koa的中间件模型,一起看看koa-compose的源码,这也是koa系列的第一篇文章,后续会更新一下koa相关的其他知识点 koa中间件的使用 先让我们启动一个koa服务 // app.js const koa = require('koa'); cons

  • AngularJS tab栏实现和mvc小案例实例详解

    tab栏: 代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Tab 标签</title> <style> body { margin: 0; padding: 0; background-color: #F7F7F7; } .tabs { width: 400px; margin: 3

  • Spring MVC的文件下载实例详解

    Spring MVC的文件下载实例详解 读取文件 要下载文件,首先是将文件内容读取进来,使用字节数组存储起来,这里使用spring里面的工具类实现 import org.springframework.util.FileCopyUtils; public byte[] downloadFile(String fileName) { byte[] res = new byte[0]; try { File file = new File(BACKUP_FILE_PATH, fileName); i

  • Java内存模型JMM详解

    Java Memory Model简称JMM, 是一系列的Java虚拟机平台对开发者提供的多线程环境下的内存可见性.是否可以重排序等问题的无关具体平台的统一的保证.(可能在术语上与Java运行时内存分布有歧义,后者指堆.方法区.线程栈等内存区域). 并发编程有多种风格,除了CSP(通信顺序进程).Actor等模型外,大家最熟悉的应该是基于线程和锁的共享内存模型了.在多线程编程中,需要注意三类并发问题: ·原子性 ·可见性 ·重排序 原子性涉及到,一个线程执行一个复合操作的时候,其他线程是否能够看

  • spring mvc路径匹配原则详解

    在Spring MVC中经常要用到拦截器,在配置需要要拦截的路径时经常用到<mvc:mapping/>子标签,其有一个path属性,它就是用来指定需要拦截的路径的.例如: <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="com.i360r.platform.webapp.runtime.view.interceptor.GenericInterceptor"

  • 对Django中内置的User模型实例详解

    User模型 User模型是这个框架的核心部分.他的完整的路径是在django.contrib.auth.models.User. 字段 内置的User模型拥有以下的字段: 1.username: 用户名.150个字符以内.可以包含数字和英文字符,以及_.@.+..和-字符.不能为空,且必须唯一! 2.first_name:歪果仁的first_name,在30个字符以内.可以为空. 3.last_name:歪果仁的last_name,在150个字符以内.可以为空. 4.email:邮箱.可以为空

随机推荐