第一次发文章,排版上简单粗糙了些。如果内容中有什么错误之处,欢迎指正。相互学习,共同提高。
本人受http://shiyousan.com/post/635383025861004585 此文启发。尊重原创。
大多数时候我们提交的web表单内容都是之前就决定好的。
比如在 csdn 个人主页中编辑简介的时候看到的表单。
这样的表单是固定的,因为它足够简单,而且被扩展的可能性也不大。
但是你总会遇到一些让你头疼的需求。
不多废话,我们直接进主题。
假如我们有一个实体类A,A中有一个属性 List<B> Member。
代码如下:

1 public class Dept 2 3 { 4 5 public int Id { get; set; } 6 7 public string Name { get; set; } 8 9 public string Description { get; set; } 10 11 public List<Employee> Members {get; set; } 12 13 } 14 15 16 17 public class Employee 18 19 { 20 21 public int Id { get; set; } 22 23 public string Name { get; set; } 24 25 public int Age { get; set; } 26 27 }
我们希望在编辑Dept的时候也可以同时编辑Members中的各个子集Employee。
我们先在Dept控制器中创建Index方法,并在其中初始化一点测试数据。具体如下:

1 public class DeptController : Controller 2 3 { 4 5 public ViewResult Index() 6 7 { 8 9 var dept = new Dept() { 10 11 Id = 1, 12 13 Name = "First Dept", 14 15 Description = string.Empty, 16 17 Members = newList<Employee>() 18 19 }; 20 21 dept.Members.Add(new Employee() { 22 23 Id = 1, 24 25 Name = "犀利的绵羊", 26 27 Age = 24 28 29 }); 30 31 return View(dept); 32 33 } 34 35 36 37 [HttpPost] 38 39 public ActionResult Index(Dept dept) 40 41 { 42 43 var membersCount = dept.Members.Count; 44 45 var t = dept; 46 47 return Json("done",JsonRequestBehavior.DenyGet); 48 49 } 50 51 }
接下来我们创建Index视图,我们可以看到Index方法中的视图模型是Dept类,在Index视图起始位置写上@model Dept(注意:如果运行出错,可以在Dept之前加上完整的命名空间,也可以在Views目录下的web.config文件中添加命名空间,类似<addnamespace="YourNamespace" />)

1 @modelDept 2 3 4 5 @using(Html.BeginForm()) 6 7 { 8 9 <div> 10 11 <table > 12 13 <caption>部门信息</caption> 14 15 <tr> 16 17 <td>名称</td> 18 19 <td>@Html.TextBoxFor(x=>x.Name)</td> 20 21 </tr> 22 23 <tr> 24 25 <td>描述</td> 26 27 <td>@Html.TextBoxFor(x=>x.Description)</td> 28 29 </tr> 30 31 <tr> 32 33 <td>成员</td> 34 35 <td> 36 37 <ul> 38 39 @for(var i = 0; i <Model.Members.Count; i++) 40 41 { 42 43 <li> 44 45 姓名:@Html.TextBoxFor(x=>x.Members[i].Name) 46 47 年龄:@Html.EditorFor(x=>x.Members[i].Age) 48 49 <inputtype="button" value="删除" class="deleteMember" /> 50 51 </li> 52 53 } 54 55 </ul> 56 57 </td> 58 59 </tr> 60 61 </table> 62 63 </div> 64 65 <input type="submit"value="保存"/> 66 67 }
我们可以看到,在处理Members时,我们用到了for循环和Html辅助方法。
生成的姓名和年龄对应的文本框的id和name属性都会包含for循环中 i 的值,也就是下标。截图如下:
下图是生成的页面
接下来就是前端的工作了,js代码如下:

1 $(function() { 2 3 init(); 4 5 }) 6 7 8 9 function init() { 10 11 $(".addMember").remove(); 12 13 $(".deleteMember").last().after("<input type='button'value='添加' class='addMember'/>"); 14 15 initAddBtn(); 16 17 initDeleteBtn(); 18 19 } 20 21 22 23 function initDeleteBtn() { 24 25 $(".deleteMember").unbind(); 26 27 $(".deleteMember").click(function() { 28 29 var memberCount =$(".deleteMember").length; 30 31 if (memberCount <= 1) { 32 33 alert("温馨提示:至少得有一个员工哦"); 34 35 return false; 36 37 } 38 39 40 41 // 修改当前员工下方的所有员工的可编辑框的属性(id, name) 42 43 $(this).parents("li").nextAll().each(function () { 44 45 // 获取输入框 46 47 var txts =$(this).children("input[type='text'], input[type='number']"); 48 49 // 获取当前员工name属性中的数字,也就是 Members 属性的下标 50 51 var index =Number(txts.eq(0).attr("name").match(/\d+/)[0]); 52 53 txts.each(function () { 54 55 // 修改属性中的数字的值, 当前员工下标 -1 56 57 $(this).attr("id",$(this).attr("id").replace(/\d+/, index - 1)); 58 59 $(this).attr("name",$(this).attr("name").replace(/\d+/, index - 1)); 60 61 }) 62 63 }) 64 65 // 删除当前 li 标签 66 67 $(this).parents("li").remove(); 68 69 init(); 70 71 }); 72 73 } 74 75 function initAddBtn() { 76 77 $(".addMember").unbind(); 78 79 $(".addMember").click(function (){ 80 81 // 复制当前 li 标签,并添加到当前 li 标签后面,形成添加效果 82 83 var parent =$(this).parents("li").clone(); 84 85 $(this).parents("li").after(parent); 86 87 88 89 // 清空复制项的各个编辑框 90 91 var txts =parent.children("input[type='text'], input[type='number']"); 92 93 txts.val(""); 94 95 96 97 // 获取复制项中“姓名”框的name属性中的数字,也就是 Members 属性的下标 98 99 var index =Number($(this).siblings().eq(0).attr("name").match(/\d+/)[0]); 100 101 txts.each(function () { 102 103 // 新增的员工的 Member 下标 +1 104 105 $(this).attr("id",$(this).attr("id").replace(/\d+/, index + 1)); 106 107 $(this).attr("name",$(this).attr("name").replace(/\d+/, index + 1)); 108 109 }) 110 111 // 删除当前 “添加” 按钮 112 113 $(this).remove(); 114 115 init(); 116 117 }) 118 119 }
需要注意的是“添加”和“删除”按钮的点击事件的处理逻辑(就好像操作List一样,删除List中的一个元素,则后方的所有元素的下标要-1, 添加的元素在尾部,其下标值是List.Count - 1)。
当我们提交这个表单的时候,Dept的其他字段不用特别在意,但是Members字段的所有下标不能间断,且需从0开始,依次递增。
下面附上调试截图为依据:(生成的html不太好看,下图中的html是经过调整的)
点击“保存”,然后我们可以看到如下的调试信息,此时的dept.Members有三个元素。而且各个元素的属性与填写的表单内容一致。
现在我们修改一下html中的一部分,再看看操作操作结果:
我们可以看到第三个元素的下标改成了3,形成了0,1,3 的不连续的情况。点击“保存”,我们看调试信息:
我们可以看到只接收到前两个元素。不连续的第三个元素已经丢失。
我们再看看第一个元素下标不为零的情况:
可以看到Members没有接收到任何元素。
现在我们证实了上面的观点。
下面我们来引申一下,如果我们试图的模型是List<Employee>而不是Dept,我们是不是也可以用这种方法做到多个元素同时编辑?我想应该是没问题的。不用那些JS插件,我们一样可以实现行内编辑哦。