
页面效果:


二、代码示例
好了,进入重点吧!博主打算分两块介绍,第一部分是表格初始化部分,第二部分是按钮操作增删改部分。
1、表格初始化
1.1、准备工作
首先看看需要引用的js和css文件
<link href="~/Content/bootstrap/css/bootstrap.min.css" rel="stylesheet" /><link href="~/Content/bootstrap-table/bootstrap-table.min.css" rel="stylesheet" /><script src="~/scripts/jquery-1.9.1.min.js"></script><script src="~/Content/bootstrap/js/bootstrap.min.js"></script><script src="~/Content/bootstrap-table/bootstrap-table.min.js"></script><script src="~/Content/bootstrap-table/locale/bootstrap-table-zh-CN.js"></script><script src="~/scripts/knockout/knockout-3.4.0.min.js"></script><script src="~/scripts/knockout/extensions/knockout.mapping-latest.js"></script><script src="~/Content/bootstrap-table/knockout.bootstraptable.js"></script><script src="~/scripts/Department.js"></script>都是一些常用的css和js文件,我们自定义的js文件主要有两个: knockout.bootstraptable.js 和 Department.js 。上篇我们介绍过使用ko可以自定义我们的data-bind。同样,这里对于table的绑定,我们也定义一个自定义的绑定,代码 knockout.bootstraptable.js 里面。
//添加ko自定义绑定ko.bindingHandlers.myBootstrapTable = {init: function (element, valueAccessor, allBindingsAccessor, viewModel) {//这里的oParam就是绑定的viewmodelvar oViewModel = valueAccessor();var $ele = $(element).bootstrapTable(oViewModel.params);//给viewmodel添加bootstrapTable方法oViewModel.bootstrapTable = function () {return $ele.bootstrapTable.apply($ele, arguments);}},update: function (element, valueAccessor, allBindingsAccessor, viewModel) {}};//初始化(function ($) {//向ko里面新增一个bootstrapTableViewModel方法ko.bootstrapTableViewModel = function (options) {var that = this;this.default = {search: true, //是否显示表格搜索,此搜索是客户端搜索,不会进服务端,所以,个人感觉意义不大strictSearch: true,showColumns: true, //是否显示所有的列cache:false,showRefresh: true, //是否显示刷新按钮minimumCountColumns: 2, //最少允许的列数clickToSelect: true, //是否启用点击选中行showToggle: true,};this.params = $.extend({}, this.default, options || {});//得到选中的记录this.getSelections = function () {var arrRes = that.bootstrapTable("getSelections")return arrRes;};//刷新this.refresh = function () {that.bootstrapTable("refresh");};};})(jQuery); 代码释疑:这个js文件主要做了两件事<table id="tb_dept" data-bind="myBootstrapTable:$root"><thead><tr><th data-checkbox="true"></th><th data-field="Name">部门名称</th><th data-field="Level">部门级别</th><th data-field="Des">描述</th><th data-field="strCreatetime">创建时间</th></tr></thead></table>代码释疑:定义一个table标签,使用自定义绑定myBootstrapTable,上篇说过,$root可以理解为初始化的意思。为了简单,所有的colums就直接在<th>里面写了。
//初始化$(function () {//1、初始化表格tableInit.Init();//2、注册增删改事件operate.operateInit();});//初始化表格var tableInit = {Init: function () {//绑定table的viewmodelthis.myViewModel = new ko.bootstrapTableViewModel({url: "/Department/GetDepartment", //请求后台的URL(*)method: "get", //请求方式(*)toolbar: "#toolbar", //工具按钮用哪个容器queryParams: function (param) {return { limit: param.limit, offset: param.offset };},//传递参数(*)pagination: true, //是否显示分页(*)sidePagination: "server", //分页方式:client客户端分页,server服务端分页(*)pageNumber: 1, //初始化加载第一页,默认第一页pageSize: 10, //每页的记录行数(*)pageList: [10, 25, 50, 100], //可供选择的每页的行数(*)});ko.applyBindings(this.myViewModel, document.getElementById("tb_dept"));}}; 代码释疑:页面加载完成之后,调用上面封装的bootstrapTableViewModel对象合并传递的参数,最后激活绑定,将this.myViewModel作为绑定的viewmodel激活。调试代码可知,当执行到 ko.applyBindings(this.myViewModel, document.getElementById("tb_dept")); 这一句的时候,自定义绑定才会生效,程序才会进入到 ko.bindingHandlers.myBootstrapTable 对象的init方法去初始化bootstrapTable。这里需要说明一点:init: function (element, valueAccessor, allBindingsAccessor, viewModel) {//这里的oParam就是绑定的viewmodelvar oViewModel = valueAccessor();var $ele = $(element).bootstrapTable(oViewModel.params);//给viewmodel添加bootstrapTable方法oViewModel.bootstrapTable = function () {return $ele.bootstrapTable.apply($ele, arguments);}}上文中的init方法,通过第二个参数valueAccessor,我们得到的是当前绑定的viewmodel,也就是我们上面的this.myViewModel这个对象,博主觉得这一点有利于你理解自定义绑定的逻辑。基本上执行到 var $ele = $(element).bootstrapTable(oViewModel.params); 这一句的时候,我们表格的初始化就完成了。后台对应的方法博主随便定义了一个集合,为了完整,这里还是贴出来:<div id="toolbar" class="btn-group"><button id="btn_add" type="button" class="btn btn-default"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span>新增</button><button id="btn_edit" type="button" class="btn btn-default"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>修改</button><button id="btn_delete" type="button" class="btn btn-default"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span>删除</button></div>为了简便,博主使用了一个隐藏的弹出框用来包含新增和编辑的文本框。当然,一般情况下,可能这里用的是部分视图,你的项目里面可能会有一个Edit.cshtml,但这里博主将这些都放在一个页面上面,因为这不是文本的重点。
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button><h4 class="modal-title" id="myModalLabel">操作</h4></div><div class="modal-body"><div class="form-group"><label for="txt_departmentname">部门名称</label><input type="text" name="txt_departmentname" data-bind="value:Name" class="form-control" id="txt_departmentname" placeholder="部门名称"></div><div class="form-group"><label for="txt_departmentlevel">部门级别</label><input type="text" name="txt_departmentlevel" data-bind="value:Level" class="form-control" id="txt_departmentlevel" placeholder="部门级别"></div><div class="form-group"><label for="txt_des">描述</label><input type="text" name="txt_des" data-bind="value:Des" class="form-control" id="txt_des" placeholder="描述"></div></div><div class="modal-footer"><button type="button" class="btn btn-default" data-dismiss="modal"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span>关闭</button><button type="button" id="btn_submit" class="btn btn-primary" data-dismiss="modal"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>保存</button></div></div></div></div>2.2、JS初始化按钮操作
//操作var operate = {//初始化按钮事件operateInit: function () {this.operateAdd();this.operateUpdate();this.operateDelete();this.DepartmentModel = {id: ko.observable(),Name: ko.observable(),Level: ko.observable(),Des: ko.observable(),CreateTime: ko.observable()};},//新增operateAdd: function(){$("#btn_add").on("click", function () {$("#myModal").modal().on("shown.bs.modal", function () {var oEmptyModel = {id: ko.observable(),Name: ko.observable(),Level: ko.observable(),Des: ko.observable(),CreateTime: ko.observable()};ko.utils.extend(operate.DepartmentModel, oEmptyModel);ko.applyBindings(operate.DepartmentModel, document.getElementById("myModal"));operate.operateSave();}).on("hidden.bs.modal", function () {ko.cleanNode(document.getElementById("myModal"));});});},//编辑operateUpdate: function () {$("#btn_edit").on("click", function () {$("#myModal").modal().on("shown.bs.modal", function () {var arrselectedData = tableInit.myViewModel.getSelections();if (!operate.operateCheck(arrselectedData)) { return; }//将选中该行数据有数据Model通过Mapping组件转换为viewmodelko.utils.extend(operate.DepartmentModel, ko.mapping.fromJS(arrselectedData[0]));ko.applyBindings(operate.DepartmentModel, document.getElementById("myModal"));operate.operateSave();}).on("hidden.bs.modal", function () {//关闭弹出框的时候清除绑定(这个清空包括清空绑定和清空注册事件)ko.cleanNode(document.getElementById("myModal"));});});},//删除operateDelete: function () {$("#btn_delete").on("click", function () {var arrselectedData = tableInit.myViewModel.getSelections();$.ajax({url: "/Department/Delete",type: "post",contentType: "application/json",data: JSON.stringify(arrselectedData),success: function (data, status) {alert(status);//tableInit.myViewModel.refresh();}});});},//保存数据operateSave: function () {$("#btn_submit").on("click", function () {//取到当前的viewmodelvar oViewModel = operate.DepartmentModel;//将Viewmodel转换为数据modelvar oDataModel = ko.toJS(oViewModel);var funcName = oDataModel.id?"Update":"Add";$.ajax({url: "/Department/"+funcName,type: "post",data: oDataModel,success: function (data, status) {alert(status);tableInit.myViewModel.refresh();}});});},//数据校验operateCheck:function(arr){if (arr.length <= 0) {alert("请至少选择一行数据");return false;}if (arr.length > 1) {alert("只能编辑一行数据");return false;}return true;}} 代码释疑:说说这里的执行逻辑,首先在$(function(){})方法里面调用 operate.operateInit(); 。在operateInit()方法里面注册页面上面按钮的点击事件,同时也定义 this.DepartmentModel 作为我们新增编辑的viewmodel,这个viewmodel里面定义了和页面元素对应的监控属性。还记得上面隐藏的弹出框里面的一些data-bind吗,没错,里面对应的value值就是和这里的监控属性对应,这样设置绑定之后,js里面所有的导致 this.DepartmentModel 里面监控的变化,都会触发界面上面这些绑定标签的value值变化,反之,界面上面的所有标签的Value值的变化,也势必会引起它的监控属性值的变化,此之所谓双向绑定。下面具体看看双向绑定的执行。$("#btn_add").on("click", function () {$("#myModal").modal().on("shown.bs.modal", function () {var oEmptyModel = {id: ko.observable(),Name: ko.observable(),Level: ko.observable(),Des: ko.observable(),CreateTime: ko.observable()};ko.utils.extend(operate.DepartmentModel, oEmptyModel);ko.applyBindings(operate.DepartmentModel, document.getElementById("myModal"));operate.operateSave();}).on("hidden.bs.modal", function () {ko.cleanNode(document.getElementById("myModal"));});}); 当我们界面触发新增操作的时候,首先会弹出上面说的隐藏模态框。在模态框显示的时候,首先定义一个空的viewmodel,然后调用 ko.utils.extend(operate.DepartmentModel, oEmptyModel); 这一句,将全局的operate.DepartmentModel被空的viewmodel覆盖。ko.utils.extend()这个方法的作用和jquery里面的$.extend()作用类似,都是根据后面对象合并前面对象,合并之后,使用新的viewmodel激活绑定。激活绑定之后,注册保存按钮的click事件。这样新增的时候,弹出模态框,由于viewmodel里面的监控属性都是空的,对应界面元素的value也会被清空,所以新增我们看到是这样:
当弹出框关闭后,我们通过关闭的事件,执行 ko.cleanNode(document.getElementById("myModal")); 这一句,这个很重要,因为对于同一个dom,ko只能绑定一次,如果需要再次绑定,需要先清空绑定,并且cleanNode()这个方法,它不仅会清空绑定,还是会dom里面注册的事件也会清空,使用的时候需要注意下!
2.4、编辑操作
$("#btn_edit").on("click", function () {$("#myModal").modal().on("shown.bs.modal", function () {var arrselectedData = tableInit.myViewModel.getSelections();if (!operate.operateCheck(arrselectedData)) { return; }//将选中该行数据有数据Model通过Mapping组件转换为viewmodelko.utils.extend(operate.DepartmentModel, ko.mapping.fromJS(arrselectedData[0]));ko.applyBindings(operate.DepartmentModel, document.getElementById("myModal"));operate.operateSave();}).on("hidden.bs.modal", function () {//关闭弹出框的时候清除绑定(这个清空包括清空绑定和清空注册事件)ko.cleanNode(document.getElementById("myModal"));});}); 当我们触发编辑操作的时候,界面还是弹出框。在弹出框的弹出事件里面,我们取到当前选中的行,然后校验是否选中了一行。最好通过 ko.mapping.fromJS(arrselectedData[0]) 这一句,将普通的Json对象转换为带有监控属性的viewmodel,上篇说过,这个方法需要 knockout.mapping-latest.js 这个js文件的支持。转换之后,还是通过ko.utils.extend()方法更新viewmodel,然后激活绑定。由于viewmodel被当前选中行的数据更新了,所以得到结果:
2.5、保存操作
在新增和编辑弹出框之后,修改相关信息后点击保存,就会触发保存事件。
$("#btn_submit").on("click", function () {//取到当前的viewmodelvar oViewModel = operate.DepartmentModel;//将Viewmodel转换为数据modelvar oDataModel = ko.toJS(oViewModel);var funcName = oDataModel.id?"Update":"Add";$.ajax({url: "/Department/"+funcName,type: "post",data: oDataModel,success: function (data, status) {alert(status);tableInit.myViewModel.refresh();}});}); 当触发保存事件的时候,我们首先取到页面绑定的viewmodel,即operate.DepartmentModel,然后使用ko.toJS()方法将带有监控属性的viewmodel转换为纯数据的Json对象,这个方法是ko内置的,不需要其他js支持。得到json对象之后,发送ajax请求,去新增或者编辑数据。这样就很好地体现了双向绑定,界面上面所有文本框的value发生了变化之后,也会触发operate.DepartmentModel的变化。