BootstrapTable与KnockoutJS相结合实现增删改查功能【二】

 更新时间:2016年05月10日 14:58:06   转载 作者:懒得安分  
这篇文章主要介绍了BootstrapTable与KnockoutJS相结合实现增删改查功能【二】的相关资料,非常具有参考价值,感兴趣的朋友一起学习吧

在上篇文章给大家介绍了BootstrapTable与KnockoutJS相结合实现增删改查功能【一】,介绍了下knockout.js的一些基础用法。接下来通过本文继续给大家介绍。如果你也打算用ko去做项目,且看看吧!

Bootstrap是一个前端框架,解放Web开发者的好东东,展现出的UI非常高端大气上档次,理论上可以不用写一行css。只要在标签中加上合适的属性即可。

KnockoutJS是一个JavaScript实现的MVVM框架。非常棒。比如列表数据项增减后,不需要重新刷新整个控件片段或自己写JS增删节点,只要预先定义模板和符合其语法定义的属性即可。简单的说,我们只需要关注数据的存取。

一、效果预览

其实也没啥效果,就是简单的增删改查,重点还是在代码上面,使用ko能够大量节省界面DOM数据绑定的操作。下面是整个整个增删改查逻辑的js代码:


页面效果:

二、代码示例

好了,进入重点吧!博主打算分两块介绍,第一部分是表格初始化部分,第二部分是按钮操作增删改部分。

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就是绑定的viewmodel
var 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文件主要做了两件事

1.自定义data-bind属性myBootstrapTable。对于ko.bindingHandlers.myBootstrapTable里面的update方法,如非必须,可以不用定义。

2.通过向ko对象里面添加bootstrapTableViewModel来封装bootstrapTable。

1.2、html标签启动绑定

<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>里面写了。

1.3、激活ko的绑定

在页面加载完成之后,启动ko的绑定:

//初始化
$(function () {
//1、初始化表格
tableInit.Init();
//2、注册增删改事件
operate.operateInit();
});
//初始化表格
var tableInit = {
Init: function () {
//绑定table的viewmodel
this.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就是绑定的viewmodel
var 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); 这一句的时候,我们表格的初始化就完成了。后台对应的方法博主随便定义了一个集合,为了完整,这里还是贴出来:

DepartmentController

2、按钮操作

上面通过bootstrapTable的初始化完成了我们的自定义data-bind的使用。下面的按钮操作我们来体验一把使用监控属性的“爽歪歪”。

2.1、view页面

首先在view页面上面定义我们的增删改按钮

<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">&times;</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组件转换为viewmodel
ko.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 () {
//取到当前的viewmodel
var oViewModel = operate.DepartmentModel;
//将Viewmodel转换为数据model
var 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值的变化,也势必会引起它的监控属性值的变化,此之所谓双向绑定。下面具体看看双向绑定的执行。

2.3、新增操作

$('#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组件转换为viewmodel
ko.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 () {
//取到当前的viewmodel
var oViewModel = operate.DepartmentModel;
//将Viewmodel转换为数据model
var 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的变化。

2.6、删除操作

删除操作没什么好说的,和ko关系不大。

三、总结

以上通过一个简单的增删改查操作,介绍了下ko和bootstrapTable的联合使用。ko可以让你从DOM中解放出来,把关注点放在viewmodel上面。纵观整个js代码,几乎看不到jquery的val()、text()等对界面dom做取值和赋值的操作,是不是看着干净清爽,并且高大上了呢~~当然,这或许只是ko的一些比较基础的用法,毕竟博主学习ko才3天,更多高级用法还有待摸索,等过段时间用熟了,再将它的一些高级用法分享给大家。如果你觉得本文能够帮助你理解ko的原理以及它的一些用法,不妨推荐下,小编在此感激不尽!

以上所述是小编给大家介绍的BootstrapTable与KnockoutJS相结合实现增删改查功能【二】的全部内容,希望对大家有所帮助!

相关文章

  • Js-$.extend扩展方法使方法参数更灵活

    Js-$.extend扩展方法使方法参数更灵活

    在JS里,我们的方法参数通常使用JQ的$.extend扩展方法来实现,感兴趣的朋友可以了解下
    2013-01-01
  • javascript实现拖放效果

    javascript实现拖放效果

    这篇文章主要介绍了javascript实现拖放效果的相关资料,需要的朋友可以参考下
    2015-12-12
  • ES6正则表达式的一些新功能总结

    ES6正则表达式的一些新功能总结

    这篇文章主要总结了一些关于ES6正则表达式的新功能的相关资料,文中介绍的还是比较详细,对大家具有一定的参考学习价值,需要的朋友们下来要起看看吧。
    2017-05-05
  • JavaScript的事件机制详解

    JavaScript的事件机制详解

    事件是将JavaScript脚本与网页联系在一起的主要方式,是JavaScript中最重要的主题之一。本文将详细探讨JavaScript的事件机制,并对比分析了浏览器之间的不同,具体内容包括事件流、事件处理程序绑定方式、事件对象等。
    2017-01-01
  • 原生js获取宽高与jquery获取宽高的方法关系对比

    原生js获取宽高与jquery获取宽高的方法关系对比

    这篇文章主要介绍了原生js获取宽高与jquery获取宽高的方法关系对比,需要的朋友可以参考下
    2014-04-04
  • js完美解决IE6不支持position:fixed的bug

    js完美解决IE6不支持position:fixed的bug

    关于IE6,虽然它已被微软抛弃很久了,但是由于大天朝的特殊行情(盗版)对于前端工程师来说,解决IE6兼容position:fixed的问题显得很重要。特别是你需要用到头尾悬停调用的时候
    2015-04-04
  • JavaScript求一组数的最小公倍数和最大公约数常用算法详解【面向对象,回归迭代和循环】

    JavaScript求一组数的最小公倍数和最大公约数常用算法详解【面向

    这篇文章主要介绍了JavaScript求一组数的最小公倍数和最大公约数常用算法,结合实例形式分析了javascript基于面向对象,回归迭代和循环等算法求解一组数的最小公倍数与最大公约数相关操作技巧,需要的朋友可以参考下
    2018-05-05
  • js变形金刚文字特效代码分享

    js变形金刚文字特效代码分享

    这篇文章主要介绍了js变形金刚文字特效,实现效果超酷,很立体,感兴趣的小伙伴们可以参考一下
    2015-08-08
  • js+canvas绘制五角星的方法

    js+canvas绘制五角星的方法

    这篇文章主要介绍了js+canvas绘制五角星的方法,涉及JavaScript调用canvas组件结合数学运算绘制图形的相关技巧,需要的朋友可以参考下
    2016-01-01
  • JS加载iFrame出现空白问题的解决办法

    JS加载iFrame出现空白问题的解决办法

    在使用IE6浏览器开发过程中出现各种奇葩问题,非常棘手,费劲脑汁终于问题解决。小编把解决办法分享到脚本之家平台,需要的朋友可以参考下
    2016-05-05

最新评论