阅读目录

  • 一、第一个viewmodel搞定查询

    • 1、后台向View返回viewmodel的实现
    • 2、cshtml页面代码
    • 3、JS封装
  • 二、第二个viewmodel搞定编辑
    • 1、ActionResult的实现
    • 2、cshtml代码
    • 3、js封装
  • 三、总结

正文

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

KnockoutJS系列文章:

  • JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(一)
  • JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(二)
  • JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(三):两个Viewmodel搞定增删改查

本文原创地址:http://www.cnblogs.com/landeanfen/p/5656307.html

回到顶部

一、第一个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;
}<!DOCTYPE html>
<html>
<head><meta name="viewport" content="width=device-width" /><title>Index</title><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="~/scripts/extensions/knockout.index.js"></script><script src="~/scripts/extensions/knockout.bootstraptable.js"></script><script type="text/javascript">$(function(){var data = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model));ko.bindingViewModel(data, document.getElementById("div_index"));});</script>
</head>
<body><div id="div_index" class="panel-body" style="padding:0px;overflow-x:hidden;"><div class="panel panel-default"><div class="panel-heading">查询条件</div><div class="panel-body"><form id="formSearch" class="form-horizontal"><div class="form-group"><label class="control-label col-xs-1">部门名称</label><div class="col-xs-3"><input type="text" class="form-control" data-bind="value:queryCondition.name"></div><label class="control-label col-xs-1">部门描述</label><div class="col-xs-3"><input type="text" class="form-control" data-bind="value:queryCondition.des"></div><div class="col-xs-4" style="text-align:right;"><button type="button"data-bind="click:clearClick" class="btn">清空</button><button type="button"data-bind="click:queryClick" class="btn btn-primary">查询</button></div></div></form></div></div><div id="toolbar" class="btn-group"><button data-bind="click:addClick" type="button" class="btn btn-default"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span>新增</button><button data-bind="click:editClick" type="button" class="btn btn-default"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>修改</button><button data-bind="click:deleteClick" type="button" class="btn btn-default"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span>删除</button></div><table data-bind="bootstrapTable:bootstrapTable"><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></div></body>
</html>

代码释疑:和上篇一样,需要引用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就是绑定的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) {}};
})(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 = $('<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"></div>');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 = $('<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"></div>');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);}

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

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

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

<script type="text/javascript">$(function(){var data = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model));data.tableParams.onLoadSuccess = function(data){          alert("加载成功事件");       };ko.bindingViewModel(data, document.getElementById("div_index"));});</script>

回到顶部

二、第二个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代码如下:

<form id="formEdit"><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:editModel.Name" class="form-control" placeholder="部门名称"></div><div class="form-group"><label for="txt_departmentlevel">部门级别</label><input type="text" name="txt_departmentlevel" data-bind="value:editModel.Level" class="form-control" placeholder="部门级别"></div><div class="form-group"><label for="txt_des">描述</label><input type="text" name="txt_des" data-bind="value:editModel.Des" class="form-control" 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="submit" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>保存</button></div></div></div>
</form>
<link href="~/Content/bootstrapValidator/css/bootstrapValidator.css" rel="stylesheet" />
<script src="~/Content/bootstrapValidator/js/bootstrapValidator.js"></script>
<script src="~/scripts/extensions/knockout.edit.js"></script>
<script type="text/javascript">$(function () {var editData = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model));ko.bindingEditViewModel(editData, {});        });
</script>

代码释疑:由于我们加了验证组件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页面的代码改成这样:

<form id="formEdit"><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="Name" data-bind="value:editModel.Name" class="form-control" placeholder="部门名称"></div><div class="form-group"><label for="txt_departmentlevel">部门级别</label><input type="text" name="Level" data-bind="value:editModel.Level" class="form-control" placeholder="部门级别"></div><div class="form-group"><label for="txt_des">描述</label><input type="text" name="Des" data-bind="value:editModel.Des" class="form-control" 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="submit" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>保存</button></div></div></div>
</form>
<link href="~/Content/bootstrapValidator/css/bootstrapValidator.css" rel="stylesheet" />
<script src="~/Content/bootstrapValidator/js/bootstrapValidator.js"></script>
<script src="~/scripts/extensions/knockout.edit.js"></script>
<script type="text/javascript">$(function () {var editData = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model));ko.bindingEditViewModel(editData, {Name: {validators: {notEmpty: {message: '名称不能为空!'}}}});        });</script>

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

注意:验证属性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<Department> oData){DepartmentModel.Delete(oData);return Json(new { }, JsonRequestBehavior.AllowGet);}

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

回到顶部

三、总结

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

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

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

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

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

可能你要说,对于单表的增删改查,实际项目中能有多少。确实,项目中大部分的业务是跨表的,但是,如果根据你的业务去建立对应的viewmodel,那么上述封装还是能起到一定简化代码的作用。还是那句话,你的需求决定了你的封装思路。如果你觉得本文能够帮到你,可以打赏博主,也可以推荐下,博主一定继续努力!

欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利

转载于:https://www.cnblogs.com/Jeely/p/10958684.html

JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(三):两个Viewmodel搞定增删改查...相关推荐

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

    前言:之前博主分享过knockoutJS和BootstrapTable的一些基础用法,都是写基础应用,根本谈不上封装,仅仅是避免了html控件的取值和赋值,远远没有将MVVM的精妙展现出来.最近项目打 ...

  2. JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(三):两个Viewmodel搞定增删改查

    JS组件系列--BootstrapTable+KnockoutJS实现增删改查解决方案(三):两个Viewmodel搞定增删改查 参考文章: (1)JS组件系列--BootstrapTable+Kno ...

  3. JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(一)

    JS组件系列--BootstrapTable+KnockoutJS实现增删改查解决方案(一) 参考文章: (1)JS组件系列--BootstrapTable+KnockoutJS实现增删改查解决方案( ...

  4. JS组件系列——BootstrapTable 行内编辑解决方案:x-editable

    JS组件系列--BootstrapTable 行内编辑解决方案:x-editable 参考文章: (1)JS组件系列--BootstrapTable 行内编辑解决方案:x-editable (2)ht ...

  5. JS组件系列——KnockoutJS用法

    前言:出于某种原因,需要学习下Knockout.js,这个组件很早前听说过,但一直没尝试使用,这两天学习了下,觉得它真心不错,双向绑定的机制简直太爽了.今天打算结合bootstrapTable和Kno ...

  6. JS组件系列——又一款MVVM组件:Vue(一:30分钟搞定前端增删改查)

    前言:关于Vue框架,好几个月之前就听说过,了解一项新技术之后,总是处于观望状态,一直在犹豫要不要系统学习下.正好最近有点空,就去官网了解了下,看上去还不错的一个组件,就抽空研究了下.最近园子里vue ...

  7. JS组件系列——自己动手扩展BootstrapTable的 冻结列 功能:彻底解决高度问题

    阅读目录 一.问题追踪 二.效果预览 三.源码解析 1.源码各个方法解释 2.对于上述抛出的ie和谷歌的兼容性问题的解析 3.项目中的使用 4.扩展 四.总结 正文 前言:一年前,博主分享过一篇关于b ...

  8. JS组件系列——表格组件神器:bootstrap table

    前言:之前一直在忙着各种什么效果,殊不知最基础的Bootstrap Table用法都没有涉及,罪过,罪过.今天补起来吧.上午博主由零开始自己从头到尾使用了一遍Bootstrap Table ,遇到不少 ...

  9. JS组件系列——Bootstrap Table 表格行拖拽(二:多行拖拽)

    原文:JS组件系列--Bootstrap Table 表格行拖拽(二:多行拖拽) 前言:前天刚写了篇JS组件系列--Bootstrap Table 表格行拖拽,今天接到新的需要,需要在之前表格行拖拽的 ...

  10. JS组件系列——Bootstrap Table 表格行拖拽

    JS组件系列--Bootstrap Table 表格行拖拽 原文:JS组件系列--Bootstrap Table 表格行拖拽 前言:之前一直在研究DDD相关知识,好久没更新JS系列文章了.这两天做了一 ...

最新文章

  1. windows系统下的python环境的搭建
  2. 1900美元,你想要机器女朋友,还是想要女朋友?
  3. android旋转动画和平移动画具体解释,补充说一下假设制作gif动画放到csdn博客上...
  4. OpenOffice+JodConverter实现Office文件到PDF的转换
  5. 共享单车再涨价,真要骑不起了!
  6. leetcode哈希表解决异位词问题
  7. 信号与线性系统管致中第六版pdf_【对讲机的那点事】无线电天馈系统中载频合路器的作用...
  8. Cisco路由器配置命令
  9. ps插件套装imagenomic磨皮滤镜安装教程
  10. oppor829t如何刷机_科普OPPO R1 R829T的线刷教程及最简单的三星手机刷机教程
  11. 乔布斯2005的演讲
  12. 七牛云存储使用经历到底怎么样
  13. 公用电信网间互联管理规定
  14. 不是所有成功都值得尊重
  15. SQL Server 损坏修复
  16. 新概念c语言能力教程答案,新概念c语言能力教程
  17. 混合策略改进的麻雀搜索算法-附代码
  18. 视频教程-2019年人工智能热门案例精讲之P图美化照片-深度学习
  19. 翻翻git之---史上最强的图片选择器 GalleryFinal(顺带附下麦麦,当当的近照)
  20. 现代化富文本编辑器 Quill Editor

热门文章

  1. Sass的安装(windows 10)
  2. shell脚本循环嵌套
  3. 易科软件中国:维系客户关系是企业的根本
  4. HashMap 在 JDK 1.8 中新增的数据结构 – 红黑树
  5. MySQL中char、varchar和text的区别
  6. everything is nothing
  7. mysql 新增字段 添加字段 删除字段 修改字段 级联删除 级联更新 等
  8. 诡异的ie8堆栈溢出异常
  9. Web 设计师的 50 个超便利工具[下]
  10. LSI SAS 3008配置操作