前言:之前博主分享过knockoutJS和BootstrapTable的一些基础用法,都是写基础应用,根本谈不上封装,仅仅是避免了html控件的取值和赋值,远远没有将MVVM的精妙展现出来。最近项目打算正式将ko用起来,于是乎对ko和bootstraptable做了一些封装,在此分享出来供园友们参考。封装思路参考博客园大神萧秦,如果园友们有更好的方法,欢迎讨论。
KnockoutJS系列文章:
demo的实现还是延续上次的部门管理功能。以下展开通过数据流向来说明。
代码释疑:这里返回的model包含三个选项
•tableParams:页面表格初始化参数。由于js里面定义了默认参数,所以这里设置的参数是页面特定的初始化参数。
•urls:包含增删改请求的url路径。
•queryCondition:页面的查询条件。
Index.cshtml页面代码如下:
代码释疑:和上篇一样,需要引用JQuery、bootstrap、bootstraptable、knockout等相关文件。这里重点说明下两个文件:
•knockout.index.js:封装了查询页面相关的属性和事件绑定。
•knockout.bootstraptable.js:封装了bootstrapTable的初始化和自定义knockout绑定的方法。
以上所有的页面交互都封装在了公共js里面,这样就不用在页面上面写大量的DOM元素取赋值、事件的绑定等重复代码,需要在本页面写的js只有以上两句,是不是很easy。
3、JS封装
重点来看看上面的说的两个js文件knockout.bootstraptable.js和knockout.index.js。
(1)knockout.bootstraptable.js
viewmodel
方法
ko.bootstrapTable
viewmodel = function (options) {
var that = this;
this.default = {
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],//可供选择的每页的行数(*)
method: 'get',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");
};
};
//
添加ko
自定义绑定
ko.bindingHandlers.bootstrapTable = {
init: function (element,valueAccessor,allBindingsAccessor,
viewmodel) {
//这里的oParam就是绑定的
viewmodel
var o
viewmodel = valueAccessor();
var $ele = $(element).bootstrapTable(o
viewmodel.params);
//给
viewmodel
添加bootstrapTable
方法
o
viewmodel.bootstrapTable = function () {
return $ele.bootstrapTable.apply($ele,arguments);
}
},update: function (element,
viewmodel) {
}
};
})(jQuery);
代码释疑:上面代码主要做了两件事
1.自定义了bootstrapTable初始化的viewmodel。
2.
添加ko自定义绑定。
如果园友不理解自定义绑定的使用,可以看看博主的前两篇博文(一)和(二),有详细介绍。
(2)knockout.index.js
viewmodel = function (data,bindElement) {
var self = this;
this.queryCondition = ko.mapping.fromJS(data.queryCondition);
this.defaultQueryParams = {
queryParams: function (param) {
var params = self.queryCondition;
params.limit = param.limit;
params.offset = param.offset;
return params;
}
};
var tableParams = $.extend({},this.defaultQueryParams,data.tableParams || {});
this.bootstrapTable = new ko.bootstrapTable
viewmodel(tableParams);
//清空事件
this.clearClick = function () {
$.each(self.queryCondition,function (key,value) {
//只有监控
属性才清空
if (typeof (value) == "function") {
this(''); //value('');
}
});
self.bootstrapTable.refresh();
};
//
查询事件
this.queryClick = function () {
self.bootstrapTable.refresh();
};
//新增事件
this.addClick = function () {
var dialog = $('
');
dialog.load(data.urls.edit,null,function () { });
$("body").append(dialog);
dialog.modal().on('hidden.bs.modal',function () {
//
关闭弹出框的时候清除绑定(这个清空
包括清空绑定和清空
注册事件)
ko.cleanNode(document.getElementById("formEdit"));
dialog.remove();
self.bootstrapTable.refresh();
});
};
//编辑事件
this.editClick = function () {
var arrselectedData = self.bootstrapTable.getSelections();
if (arrselectedData.length <= 0 || arrselectedData.length > 1) {
alert("每次只能编辑一行");
return;
}
var dialog = $('
');
dialog.load(data.urls.edit,arrselectedData[0],function () {
//
关闭弹出框的时候清除绑定(这个清空
包括清空绑定和清空
注册事件)
ko.cleanNode(document.getElementById("formEdit"));
dialog.remove();
self.bootstrapTable.refresh();
});
};
//
删除事件
this.deleteClick = function () {
var arrselectedData = self.bootstrapTable.getSelections();
if (!arrselectedData||arrselectedData.length<=0) {
alert("请至少选择一行");
return;
}
$.ajax({
url: data.urls.delete,type: "post",contentType: 'application/json',data: JSON.stringify(arrselectedData),success: function (data,status) {
alert(status);
self.bootstrapTable.refresh();
}
});
};
ko.applyBindings(self,bindElement);
};
})(jQuery);
代码释疑:这个js主要封装了页面元素的属性和事件绑定,需要说明的几个地方
•this.queryCondition = ko.mapping.fromJS(data.queryCondition):这一句的作用是将后台传过来的查询条件,从JSON数据转换成监控属性。只有执行了这一句,属性和页面元素才能双向监控。
•self.bootstrapTable.refresh():这一句的含义是刷新表格数据,它实际上是调用的bootstrapTable的refresh方法,只不过博主在knockout.bootstraptable.js文件里面对它进行了简单封装。
•dialog.load(data.urls.edit,function () { }):在新增和编辑的时候使用了jQuery的load()方法,这个方法的作用是请求这个url的页面元素,并执行url对应页面的js代码。此方法在动态引用js文件并执行js文件里面代码这方面功能很强大。
最后附上后台GetDepartment()方法对应的代码
x.Name.Contains(name)).ToList();
}
if (!string.IsNullOrEmpty(des))
{
lstRes = lstRes.Where(x => x.Des.Contains(des)).ToList();
}
lstRes.ForEach(x=> {
x.strCreatetime = x.Createtime.ToString("yyyy-MM-dd HH:mm:ss");
});
var oRes = new
{
rows = lstRes.Skip(offset).Take(limit).ToList(),total = lstRes.Count
};
return Json(oRes,JsonRequestBehavior.AllowGet);
}
至此,查询页面的查询、清空功能即可实现。
你是否还有一个疑问:如果我们需要自定义bootstrapTable的事件怎么办?不能通过后台的viewmodel传过来吧?
确实,从后台是无法传递js事件方法的,所以需要我们在前端自定义事件的处理方法,比如我们可以这样:
viewmodel(data,document.getElementById("div_index"));
});
上面的一个viewmodel搞定了查询和删除的功能,但是新增和编辑还需要另一个viewmodel的支持。下面来看看编辑的封装实现。
1、ActionResult的实现
通过上面查询的代码我们可以知道,当用户点击新增和编辑的时候,会请求另一个View视图→/Department/Edit。下面来看看Edit视图的实现
代码释疑:上述代码很简单,就是向视图页面返回一个viewmodel,包含编辑的实体和提交的url。通过这个实体主键是否存在来判断当前提交是新增实体还是编辑实体。
Edit.csHTML代码如下:
名称
名称">
代码释疑:由于我们加了验证组件bootstrapValidator,所以需要引用相关js和css。knockout.edit.js这个文件主要封装了编辑页面的属性和事件绑定。重点来看看这个js的实现代码。
3、js封装
knockout.edit.js代码:
viewmodel = function (data,validatorFields) {
var that = {};
that.editModel = ko.mapping.fromJS(data.editModel);
that.default = {
message: '验证不通过',fields: { },submitHandler: function (validator,form,submitButton) {
var arrselectedData = ko.toJS(that.editModel);
$.ajax({
url: data.urls.submit,status) {
alert(status);
}
});
$("#myModal").modal("hide");
}
};
that.params = $.extend({},that.default,{fields: validatorFields} || {});
$('#formEdit').bootstrapValidator(that.params);
ko.applyBindings(that,document.getElementById("formEdit"));
};
})(jQuery);
代码释疑:这个js主要封装了编辑模型的属性和提交的事件绑定。由于用到了bootstrapValidator验证组件,所以需要表单提交。其实公共js里面是不应该出现页面id的,比如上面的“formEdit”和“myModal”,可以将此作为参数传过来,这点有待优化。参数validatorFields表示验证组件的验证字段,如果表单不需要验证,则传一个空的Json或者不传都行。上文我们没有做字段验证,其实一般来说,基础表都会有一个或者几个非空字段,比如我们可以加上部门名称的非空验证。在Edit.cshtml页面的代码改成这样:
名称
名称">