html编辑ko,BootstrapTable+KnockoutJS相结合实现增删改查解决方案(三)两个Viewmodel搞定增删改查...

本文介绍如何结合BootstrapTable与KnockoutJS实现前端的增删改查功能,通过两个ViewModel分别完成查询与编辑操作,简化了页面交互的开发流程。

前言:之前博主分享过knockoutJS和BootstrapTable的一些基础用法,都是写基础应用,根本谈不上封装,仅仅是避免了html控件的取值和赋值,远远没有将MVVM的精妙展现出来。最近项目打算正式将ko用起来,于是乎对ko和bootstraptable做了一些封装,在此分享出来供园友们参考。封装思路参考博客园大神萧秦,如果园友们有更好的方法,欢迎讨论。

KnockoutJS系列文章:

一、第一个viewmodel搞定查询

demo的实现还是延续上次的部门管理功能。以下展开通过数据流向来说明。

1、后台向View返回viewmodel的实现

public ActionResult Index()

{

var model = new

{

tableParams = new

{

url = "/Department/GetDepartment",

//pageSize = 2,

},

urls = new

{

delete = "/Department/Delete",

edit = "/Department/Edit",

add = "/Department/Edit",

},

queryCondition = new

{

name = "",

des = ""

}

};

return View(model);

}

代码释疑:这里返回的model包含三个选项

•tableParams:页面表格初始化参数。由于js里面定义了默认参数,所以这里设置的参数是页面特定的初始化参数。

•urls:包含增删改请求的url路径。

•queryCondition:页面的查询条件。

2、cshtml页面代码

Index.cshtml页面代码如下:

@{

Layout = null;

}

Index

$(function(){

var data = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model));

ko.bindingViewModel(data, document.getElementById("div_index"));

});

查询条件

部门名称

部门描述

清空

查询

新增

修改

删除

部门名称部门级别描述创建时间

代码释疑:和上篇一样,需要引用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

(function ($) {

//向ko里面新增一个bootstrapTableViewModel方法

ko.bootstrapTableViewModel = 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 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) {

}

};

})(jQuery);

代码释疑:上面代码主要做了两件事

1.自定义了bootstrapTable初始化的ViewModel。

2.

添加ko自定义绑定。

如果园友不理解自定义绑定的使用,可以看看博主的前两篇博文(一)和(二),有详细介绍。

(2)knockout.index.js

(function ($) {

ko.bindingViewModel = 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.bootstrapTableViewModel(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 () { });

$("body").append(dialog);

dialog.modal().on('hidden.bs.modal', 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, null, function () { }):在新增和编辑的时候使用了jQuery的load()方法,这个方法的作用是请求这个url的页面元素,并执行url对应页面的js代码。此方法在动态引用js文件并执行js文件里面代码这方面功能很强大。

最后附上后台GetDepartment()方法对应的代码

[HttpGet]

public JsonResult GetDepartment(int limit, int offset, string name, string des)

{

var lstRes = DepartmentModel.GetData();

if (!string.IsNullOrEmpty(name))

{

lstRes = lstRes.Where(x => 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);

}

至此,查询页面的查询、清空功能即可实现。

3b453c54b60647692c7736a5ae4e22cb.gif

你是否还有一个疑问:如果我们需要自定义bootstrapTable的事件怎么办?不能通过后台的viewmodel传过来吧?

确实,从后台是无法传递js事件方法的,所以需要我们在前端自定义事件的处理方法,比如我们可以这样:

$(function(){

var data = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model));

data.tableParams.onLoadSuccess = function(data){          alert("加载成功事件");       };

ko.bindingViewModel(data, document.getElementById("div_index"));

});

二、第二个viewmodel搞定编辑

上面的一个viewmodel搞定了查询和删除的功能,但是新增和编辑还需要另一个viewmodel的支持。下面来看看编辑的封装实现。

1、ActionResult的实现

通过上面查询的代码我们可以知道,当用户点击新增和编辑的时候,会请求另一个View视图→/Department/Edit。下面来看看Edit视图的实现

public ActionResult Edit(Department model)

{

var oResModel = new

{

editModel = model,

urls = new

{

submit = model.id == 0 ? "/Department/Add" : "/Department/Update"

}

};

return View(oResModel);

}

代码释疑:上述代码很简单,就是向视图页面返回一个viewmodel,包含编辑的实体和提交的url。通过这个实体主键是否存在来判断当前提交是新增实体还是编辑实体。

2、cshtml代码

Edit.cshtml代码如下:

$(function () {

var editData = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model));

ko.bindingEditViewModel(editData, {});

});

代码释疑:由于我们加了验证组件bootstrapValidator,所以需要引用相关js和css。knockout.edit.js这个文件主要封装了编辑页面的属性和事件绑定。重点来看看这个js的实现代码。

3、js封装

knockout.edit.js代码:

(function ($) {

ko.bindingEditViewModel = 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,

type: "post",

contentType: 'application/json',

data: JSON.stringify(arrselectedData),

success: function (data, 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页面的代码改成这样:

$(function () {

var editData = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model));

ko.bindingEditViewModel(editData, {

Name: {

validators: {

notEmpty: {

message: '名称不能为空!'

}

}

}

});

});

那么就会在提交的时候自动进行验证:

2617538b6050a1bee4b5a5fc56105a6e.png

注意:验证属性Name对应的是input标签的name属性,所以要做验证,这个name属性必须设置正确。

最好附上增删改的后台方法:

[HttpPost]

public JsonResult Add(Department oData)

{

DepartmentModel.Add(oData);

return Json(new { }, JsonRequestBehavior.AllowGet);

}

[HttpPost]

public JsonResult Update(Department oData)

{

DepartmentModel.Update(oData);

return Json(new { }, JsonRequestBehavior.AllowGet);

}

[HttpPost]

public JsonResult Delete(List oData)

{

DepartmentModel.Delete(oData);

return Json(new { }, JsonRequestBehavior.AllowGet);

}

至此,我们整个页面的增删改查效果就OK了,简单看下效果:

82b8ba549e33ed6002fc09ee97b249a9.gif

三、总结

以上简单封装了bootstrapTable+ko的增删改查业务,只是一个最初级的封装。如果你需要将这些运用都你的项目中,可能还需要一些简单的优化措施,比如:

1、如果单纯是一个页面的viewmodel,是否可以不用从后台的ActionResult里面返回,直接写在View页面里面感觉更好,省去了序列化和参数传递的问题。这点有待优化。

2、公共js里面不应该出现页面元素的id,可以通过参数将页面元素传递进来。

3、新增和编辑事件方法里面弹出框的部分有很多重复代码,这部分的最好做法是将弹出框封装成一个单独的组件去调用,可以减少大部分的js代码。

4、如果查询条件以及编辑的属性里面存在select下拉框元素,可能还需要封装下拉框的datasourse等属性,这一部分是非常常见的,等博主整理好demo后将这块加进去。

以上所述是小编给大家介绍的BootstrapTable+KnockoutJS相结合实现增删改查解决方案(三)两个Viewmodel搞定增删改查 ,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

JS组件Bootstrap Table使用方法详解 转载 2016年03月21日 15:06:09 标签: Bootstrap Table 最近客户提出需求,想将原有的管理系统,做下优化,通过手机也能很好展现,想到2个方案: a方案:保留原有的页面,新设计一套适合手机的页面,当手机访问时,进入m.zhy.com(手机页面),pc设备访问时,进入www.zhy.com(pc页面) b方案:采用bootstrap框架,替换原有页面,自动适应手机、平板、PC 设备 采用a方案,需要设计一套界面,并且要得重新写适合页面的接口,考虑到时间及成本问题,故项目采用了b方案 一、效果展示 二、BootStrap table简单介绍 bootStrap table 是一个轻量级的table插件,使用AJAX获取JSON格式的数据,其分页和数据填充很方便,支持国际化 、使用方法 1、引入js、css [js] view plain copy <!--css样式--> <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet"> <link href="css/bootstrap/bootstrap-table.css" rel="stylesheet"> <!--js--> [removed][removed] [removed][removed] [removed][removed] [removed][removed] 2、table数据填充 bootStrap table获取数据有两种方式,一是通过table 的data-url属性指定数据源,二是通过JavaScript初始化表格时指定url来获取数据 [xhtml] view plain copy ... ... [xhtml] view plain copy $('#table').bootstrapTable({ url: 'data.json' }); 第二种方式交第一种而言在处理复杂数据时更为灵活,一般使用第二种方式来进行table数据填充。 [js] view plain copy $(function () { //1.初始化Table var oTable = new TableInit(); oTable.Init(); //2.初始化Button的点击事件 /* var oButtonInit = new ButtonInit(); oButtonInit.Init(); */ }); var TableInit = function () { var oTableInit = new Object(); //初始化Table oTableInit.Init = function () { $('#tradeList').bootstrapTable({ url: '/VenderManager/TradeList', //请求后台的URL(*) method: 'post', //请求方式(*) toolbar: '#toolbar', //工具按钮用哪个容器 striped: true, //是否显示行间隔色 cache: false, //是否使用缓存,默认为true,所以一般情况下需要设置一下这个属性(*) pagination: true, //是否显示分页(*) sortable: false, //是否启用排序 sortOrder: "asc", //排序方式 queryParams: oTableInit.queryParams,//传递参数(*) sidePagination: "server", //分页方式:client客户端分页,server服务端分页(*) pageNumber:1, //初始化加载第一页,默认第一页 pageSize: 50, //每页的记录行数(*) pageList: [10, 25, 50, 100], //可供选择的每页的行数(*) strictSearch: true, clickToSelect: true, //是否启用点击选中行 height: 460, //行高,如果没有设置height属性,表格自动根据记录条数觉得表格高度 uniqueId: "id", //每一行的唯一标识,一般为主键列 cardView: false, //是否显示详细视图 detailView: false, //是否显示父子表 columns: [{ field: 'id', title: '序号' }, { field: 'liushuiid', title: '交易编号' }, { field: 'orderid', title: '订单号' }, { field: 'receivetime', title: '交易时间' }, { field: 'price', title: '金额' }, { field: 'coin_credit', title: '投入硬币' }, { field: 'bill_credit', title: '投入纸币' }, { field: 'changes', title: '找零' }, { field: 'tradetype', title: '交易类型' },{ field: 'goodmachineid', title: '货机号' },{ field: 'inneridname', title: '货道号' },{ field: 'goodsName', title: '商品名称' }, { field: 'changestatus', title: '支付' },{ field: 'sendstatus', title: '出货' },] }); }; //得到查询的参数 oTableInit.queryParams = function (params) { var temp = { //这里的键的名字和控制器的变量名必须一直,这边改动,控制器也需要改成一样的 limit: params.limit, //页面大小 offset: params.offset, //页码 sdate: $("#stratTime").val(), edate: $("#endTime").val(), sellerid: $("#sellerid").val(), orderid: $("#orderid").val(), CardNumber: $("#CardNumber").val(), maxrows: params.limit, pageindex:params.pageNumber, portid: $("#portid").val(), CardNumber: $("#CardNumber").val(), tradetype:$('input:radio[name="tradetype"]:checked').val(), success:$('input:radio[name="success"]:checked').val(), }; return temp; }; return oTableInit; }; field字段必须与服务器端返回的字段对应才会显示出数据。 3、后台获取数据 a、servlet获取数据 [js] view plain copy BufferedReader bufr = new BufferedReader( new InputStreamReader(request.getInputStream(),"UTF-8")); StringBuilder sBuilder = new StringBuilder(""); String temp = ""; while((temp = bufr.readLine()) != null){ sBuilder.append(temp); } bufr.close(); String json = sBuilder.toString(); JSONObject json1 = JSONObject.fromObject(json); String sdate= json1.getString("sdate");//通过此方法获取前端数据 ... b、springMvc Controller里面对应的方法获取数据 [js] view plain copy public JsonResult GetDepartment(int limit, int offset, string orderId, string SellerId,PortId,CardNumber,Success,maxrows,tradetype) { ... } 4、分页(遇到问题最多的) 使用分页,server端返回的数据必须包括rows和total,代码如下: [js] view plain copy ...gblst = SqlADO.getTradeList(sql,pageindex,maxrows); JSONArray jsonData=new JSONArray(); JSONObject jo=null; for (int i=0,len=gblst.size();i<len;i++) { TradeBean tb = gblst.get(i); if(tb==null) { continue; } jo=new JSONObject(); jo.put("id", i+1); jo.put("liushuiid", tb.getLiushuiid()); jo.put("price", String.format("%1.2f",tb.getPrice()/100.0)); jo.put("mobilephone", tb.getMobilephone()); jo.put("receivetime", ToolBox.getYMDHMS(tb.getReceivetime())); jo.put("tradetype", clsConst.TRADE_TYPE_DES[tb.getTradetype()]); jo.put("changestatus", (tb.getChangestatus()!=0)?"成功":"失败"); jo.put("sendstatus", (tb.getSendstatus()!=0)?"成功":"失败"); jo.put("bill_credit", String.format("%1.2f",tb.getBill_credit()/100.0)); jo.put("changes",String.format("%1.2f",tb.getChanges()/100.0)); jo.put("goodroadid", tb.getGoodroadid()); jo.put("SmsContent", tb.getSmsContent()); jo.put("orderid", tb.getOrderid()); jo.put("goodsName", tb.getGoodsName()); jo.put("inneridname", tb.getInneridname()); jo.put("xmlstr", tb.getXmlstr()); jsonData.add(jo); } int TotalCount=SqlADO.getTradeRowsCount(sql); JSONObject jsonObject=new JSONObject(); jsonObject.put("rows", jsonData);//JSONArray jsonObject.put("total",TotalCount);//总记录数 out.print(jsonObject.toString()); ... 5、分页界面内容介绍 前端获取分页数据,代码如下: [js] view plain copy ...oTableInit.queryParams = function (params) { var temp = { //这里的键的名字和控制器的变量名必须一直,这边改动,控制器也需要改成一样的 limit: params.limit, //第几条记录 offset: params.offset, //显示一页多少记录 sdate: $("#stratTime").val(), }; return temp; };... 后端获取分页数据,代码如下: [js] view plain copy ...int pageindex=0; int offset = ToolBox.filterInt(json1.getString("offset")); int limit = ToolBox.filterInt(json1.getString("limit")); if(offset !=0){ pageindex = offset/limit; } pageindex+= 1;//第几页... 以上就是为大家分享的Bootstrap Table使用方法,希望对大家熟练掌握Bootstrap Table使用方法有所帮助。 转自:http://www.jb51.net/article/79033.htm
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值