最早接触到knockoutJs应该是2017年初了。彼时只是了解了一下大概信息,写了一些简单的例子,没有怎么实际使用过。现在有机会再项目中实战一下,记录一下。
1.项目架构
前端:bootstrap和layer负责页面展示,knockoutJs负责mvvm,引入jquery.validate做表单校验,还有一些其他不怎么重要的,这里没有涉及到,不介绍
后端:ssm框架
2.功能介绍
做一个工作流管理的菜单,没有采用图像化的做法,直接做数据库的增删改查。包括:工作流信息、工作流节点信息、节点关联关系
3.引入knockoutJs

因为是后期功能,因此没有在项目全局引用,只在涉及到的功能中引用了

<%--knockoutJS,mvvm框架的前端部分,可以实现页面数据和js对象的相互映射--%>
<script type="text/javascript" src="${pageContext.request.contextPath}/home/js/knockout-3.4.2.js"></script>
<%--knockoutJS的一个帮助工具:将普通的json对象转为knocoutJS所需的对象--%>
<script type="text/javascript" src="${pageContext.request.contextPath}/home/js/knockout.mapping.js"></script>
knockout.mapping.js是一个knockout的帮助工具。后边会说明,使用时,必须放在knockoutJS之后
4.前端JSP
基本结构如下
每一个div对应js中的一个ViewModel
代码如下,其中涉及到knockout的标签是data-bind=“”
data-bind="click: $root.init"    表示给元素绑定点击事件$root.init,$root代表的是绑定到该div的viewmodel,后边讲解js代码的时候会提到
data-bind="textinput:queryFlowName"    表示input中的value对应ko监控的值 queryFlowName,textinpu是在form中可以输入的元素中使用的,如果只是显示的元素,如td,则用text(data-bind="text: flowName")
foreach循环如下:
<tbody id="linkTbody" data-bind="foreach: linkItems"><tr><td data-bind="text: $index() + 1"></td><td><a href="#" data-bind="click : $root.delLink">删除</a><a href="#" data-bind="click : $root.updLink">修改</a></td><td data-bind="text: linkName"></td><td data-bind="text: nodeName"></td><td data-bind="text: nextNodeName"></td><td data-bind="text: conditionStatus"></td></tr></tbody>
其中data-bind="foreach: linkItems"表示要迭代的数组名称是viewmodel中的linkItems
data-bind="text: $index() + 1"    循环时会有一个默认的索引$index(),从0开始
后边的linkName、nodeName等都是linkItems中的对象中拥有的key
绑定多个事件
data-bind="event : {click : $root.trClick, mouseover : $root.trMouseOver, mouseout : $root.trMouseout}"
<%--knockoutJS,mvvm框架的前端部分,可以实现页面数据和js对象的相互映射--%>
<script type="text/javascript" src="${pageContext.request.contextPath}/home/js/knockout-3.4.2.js"></script>
<%--knockoutJS的一个帮助工具:将普通的json对象转为knocoutJS所需的对象--%>
<script type="text/javascript" src="${pageContext.request.contextPath}/home/js/knockout.mapping.js"></script>
<style>#linkEditDiv td:nth-child(odd){text-align: right;}#linkEditDiv td:nth-child(even){text-align: left;}#linkEditDiv tr{height:50px;}
</style>
<%--流程列表--%>
<div class="row"><div class="col-md-12" style="text-align:left" id='sysFlowManagePage'><!-- 按钮工具栏 --><ol class="breadcrumb" style="height:25px;padding-top:0;"><li><a href="javascript:void(0);" data-bind="click: $root.init"><span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 搜索</a></li><li><a href="javascript:void(0);" data-bind="click: $root.addFlow"><span class="glyphicon glyphicon-plus" style=" font-weight:bold;"></span> 新增</a></li></ol><form id="queryPage"><table style="width:100%;" align="center"><caption class="table-caption"></caption><tbody><tr><td class="query_td_right">流程名称:</td><td class="query_td_left"><input type="text" class="txt query-text" name="flowName" data-bind="textinput:queryFlowName"></td></tr></tbody></table></form><br/><!-- 查询条件table end--><!-- 列表 --><div class="table-responsive"><table id="cpaAcctAppList" align="center" class="table table-striped table-bordered table-hover table-fix"><thead><tr><th width="50">序号 </th><th width="70">操作 </th><th width="150">流程名称</th><th width="150">流程描述</th></tr></thead><tbody data-bind="foreach: items"><tr><td data-bind="text: $index() + 1"></td><td><a href="#" data-bind="click: $root.updFlow">修改</a><a href="#" data-bind="click: $root.delFlow">删除</a><a href="#" data-bind="click: $root.opeFlowLink">流程连接</a><a href="#" data-bind="click: $root.opeFlowNode">流程节点</a></td><td data-bind="text: flowName"></td><td data-bind="text: flowDesc"></td></tr></tbody></table></div></div>
</div><%--修改流程信息--%>
<div id="flowEditDiv" style="display: none;height:140px;" class="layer_notice"><ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;"><li><a data-bind="click : $root.ok" href="javascript:void(0);"><span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 保存</a></li><li><a data-bind="click : $root.no" href="javascript:void(0);"><span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 取消</a></li></ol><form role="form" id="updFlowForm"><div style="margin-top: 20px"><label>流程名称:</label><input data-bind="textinput:flowItem.flowName" type="text" name="roleName" style="width: 180px;"/></div><div style="padding:10px 0 10px 0"><label>流程描述:</label><input data-bind="textinput:flowItem.flowDesc" type="text" name="roleDesc" style="width: 180px;"/></div></form>
</div>
<%--显示连接列表--%>
<div id="flowLinkDiv" style="display: none;height:300px;margin:0 auto;" class="layer_notice"><ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;"><li><a data-bind="click : $root.addLink" href="javascript:void(0);"><span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 新增</a></li></ol><div class="table-responsive" style="width:800px;margin:10px auto;"><table id="linkTable" align="center" class="table table-striped table-bordered table-hover table-fix"><thead><tr><th width="50">序号</th><th width="70">操作</th><th width="150">连接名称</th><th width="150">节点名称</th><th width="150">下一个节点名称</th><th width="100">条件状态</th></tr></thead><tbody id="linkTbody" data-bind="foreach: linkItems"><tr><td data-bind="text: $index() + 1"></td><td><a href="#" data-bind="click : $root.delLink">删除</a><a href="#" data-bind="click : $root.updLink">修改</a></td><td data-bind="text: linkName"></td><td data-bind="text: nodeName"></td><td data-bind="text: nextNodeName"></td><td data-bind="text: conditionStatus"></td></tr></tbody></table></div>
</div><%--修改节点信息--%>
<div id="nodeEditDiv" style="display: none;height:190px;" class="layer_notice"><ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;"><li><a data-bind="click : $root.ok" href="javascript:void(0);"><span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 保存</a></li><li><a data-bind="click : $root.no" href="javascript:void(0);"><span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 取消</a></li></ol><form role="form" id="updNodeForm"><table style="margin: 15px 40px;"><tr style="height: 50px;"><td>节点名称:</td><td><input data-bind="textinput:nodeItem.nodeName" type="text" name="" style="width: 180px;" required/></td><td>所属流程:</td><td><input data-bind="textinput:nodeItem.flowName" type="text" name="flowName" style="width: 180px;" readonly required/><span class="glyphicon glyphicon-search" data-bind="click:$root.selFlow"/></td></tr><tr style="height: 50px;"><td>节点描述:</td><td><input data-bind="textinput:nodeItem.nodeDesc" type="text" name="nodeDesc" style="width: 180px;"/></td><td>节点类型:</td><td><input data-bind="textinput:nodeItem.nodeType" type="text" name="_nodeType" style="width: 180px;" required/></td></tr></table></form>
</div><%--修改连接信息--%>
<div id="linkEditDiv" style="display: none;height:190px;;" class="layer_notice"><ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;"><li><a data-bind="click : $root.ok" href="javascript:void(0);"><span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 保存</a></li><li><a data-bind="click : $root.no" href="javascript:void(0);"><span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 取消</a></li></ol><form role="form" id="updLinkForm"><table style="margin: 15px 40px;"><tr><td>流转名称:</td><td><input data-bind="textinput:item.linkName" type="text" name="linkName" style="width: 180px;" required/></td><td>本节点名称:</td><td><input data-bind="textinput:item.nodeName" type="text" name="_nodeName" style="width: 180px;" readonly required/><span class="glyphicon glyphicon-search" data-bind="click:$root.selNode" desc="node"/></td></tr><tr><td>流转描述:</td><td><input data-bind="textinput:item.linkDesc" type="text" name="linkDesc" style="width: 180px;"/></td><td>下个节点名称:</td><td><input data-bind="textinput:item.nextNodeName" type="text" name="nextNodeName" style="width: 180px;" readonly required/><span class="glyphicon glyphicon-search" data-bind="click:$root.selNextNode" desc="nextNode"/></td></tr><tr><td>条件状态:</td><td><input data-bind="textinput:item.conditionStatus" type="text" name="conditionStatus" style="width: 180px;" required/></td><td></td><td></td></tr></table></form>
</div><%--选择节点--%>
<div id="selNode" style="display: none;height:50px;margin:0 auto;" class="layer_notice"><ol class="breadcrumb" style="height:25px;padding-top:0;text-align: left;"><li><a data-bind="click : $root.addNode" href="javascript:void(0);"><span class="glyphicon glyphicon-search" style=" font-weight:bold;"></span> 新增</a></li></ol><div class="table-responsive" style="width: 800px;margin: 10px auto;"><table id="nodeTable" align="center" class="table table-striped table-bordered table-hover table-fix"><thead><tr><th width="50">序号</th><th width="60">操作</th><th width="150">节点名称</th><th width="50">节点类型</th><th width="150">节点描述</th></tr></thead><tbody id="nodeTbody" data-bind="foreach: nodeItems"><tr data-bind="event : {click : $root.trClick, mouseover : $root.trMouseOver, mouseout : $root.trMouseout}"><td data-bind="text: $index() + 1"></td><td><a href="javascript:void(0)" data-bind="click : $root.updNode">修改</a><a href="javascript:void(0)" data-bind="click : $root.delNode">删除</a></td><td data-bind="text: nodeName"></td><td data-bind="text: nodeType"></td><td data-bind="text: nodeDesc"></td></tr></tbody></table></div>
</div><%--选择流程--%>
<div id="selFlow" style="display: none;height:50px;margin:0 auto;" class="layer_notice"><div class="table-responsive" style="width: 800px;margin: 10px auto;"><table id="flowTable" align="center" class="table table-striped table-bordered table-hover table-fix"><thead><tr><th width="50">序号</th><%--<th width="60">操作</th>--%><th width="150">流程名称</th><th width="150">流程描述</th></tr></thead><tbody id="flowTbody" data-bind="foreach: flowItems"><tr data-bind="event : {click : $root.trClick, mouseover : $root.trMouseOver, mouseout : $root.trMouseout}"><td data-bind="text: $index() + 1"></td><%--<td>--%><%--<a href="javascript:void(0)" data-bind="click : $root.updNode">修改</a>--%><%--<a href="javascript:void(0)" data-bind="click : $root.delNode">删除</a>--%><%--</td>--%><td data-bind="text: flowName"></td><td data-bind="text: flowDesc"></td></tr></tbody></table></div>
</div><script type="text/javascript" src="${pageContext.request.contextPath}/js/sys_flow/sys_flow.js"></script>
5.前端js
基本结构如下

代码如下

(function () {var linkUpdLayer,selNodeLayer,selFlowLayer,flowUpdLayer,flowList ,baseUrl = contentPath + '/sysFlow/';$(function () {var vm = new TableViewModel();ko.applyBindings(vm, $('#sysFlowManagePage')[0]);vm.init();});/*** 流程节点ViewModel* @param linkItemKo 连接信息* @param flowItemKo 流程信息* @param t 类型* @constructor*/function NodeListViewModel(linkItemKo,flowItemKo,t) {var self = this;self.linkItem = linkItemKo;self.flowItem = flowItemKo;self.nodeItems = ko.observableArray([]);//是否是流程操作self.isLink = (function () {return !(linkItemKo === null || !linkItemKo);})();//初始化节点列表self.initNodeTable = function () {$.postAjax(baseUrl+'queryFlowNode',{flowId:self.isLink?linkItemKo.flowId():flowItemKo.flowId()},function (data) {self.nodeItems(data);});};//鼠标移入事件self.trMouseOver = function (item,event) {if(!self.isLink){return;}trMouseOver($(event.target));};//鼠标移出事件self.trMouseout = function(){if(!self.isLink){return;}trMouseOut($(event.target));};//鼠标点击事件self.trClick = function (item) {if(!self.isLink){return;}switch (t){case 1:self.linkItem.nodeName(item.nodeName);self.linkItem.nodeId(item.nodeId);break;case 2:self.linkItem.nextNodeName(item.nodeName);self.linkItem.nextNodeId(item.nodeId);break;}if(selNodeLayer){layer.close(selNodeLayer);}};self.addNode = function () {var newNodeItem = {flowId: '',flowName : '',nodeName: '', nodeDesc: '',nodeType:''};var nodeItemKo = new NodeEditViewModel(ko.mapping.fromJS(newNodeItem),self);nodeOpenCommon('新增节点信息',nodeItemKo);};//删除节点self.delNode = function(item){if($.isEmpty(item.nodeId)){layer.alert('发生错误!');return;}var con = layer.confirm('确定删除该记录吗?', {btn: ['确定','取消'] //按钮}, function(){layer.close(con);$.postAjax(baseUrl+'delNode',{nodeId : item.nodeId},function (state) {if(state.success){layer.msg('删除成功!');self.initNodeTable();}else{layer.alert('删除失败!失败原因:' + state.message);}});}, function(){});};//修改节点self.updNode = function (item) {if(flowList){for(var i=0;i<flowList.length;i++){if(item.flowId === flowList[i].flowId){item.flowName = flowList[i].flowName;break;}}}var updNodeVm = new NodeEditViewModel(ko.mapping.fromJS(item),self);nodeOpenCommon('修改节点信息',updNodeVm);};/*** 节点信息修改ViewModel* @param nodeItemKo* @param nodeTableViewModel* @constructor*/function NodeEditViewModel(nodeItemKo,nodeTableViewModel) {var nodeSelf = this;nodeSelf.nodeItem = nodeItemKo;//取消nodeSelf.no = function () {closeNodeOpenCommon();};//选择流程nodeSelf.selFlow = function (item) {var selFlow = document.getElementById('selFlow'),$flowTbody = $('#flowTbody'),flowTbodyHtml = $flowTbody.html(),flowListViewModel = new FlowListViewModel(ko.mapping.fromJS(item));selFlowLayer = layer.open({title: '选择流程',type: 1,content: $(selFlow),area: ['880px', '460px'],success : function () {ko.cleanNode(selFlow);ko.applyBindings(flowListViewModel, selFlow);flowListViewModel.initFlowTable();},end : function () {$flowTbody.html(flowTbodyHtml);$(selFlow).hide();}});};/*** 选择流程* @constructor*/function FlowListViewModel(nodeItemKo) {var self = this;self.nodeItem = nodeItemKo;self.flowItems = ko.observableArray([]);//初始化列表self.initFlowTable = function () {$.postAjax(baseUrl + 'queryFlow', null, function (data) {self.flowItems(data);});};//鼠标移入事件self.trMouseOver = function () {trMouseOver($(event.target));};//鼠标移出事件self.trMouseout = function(){trMouseOut($(event.target));};//鼠标点击事件self.trClick = function (item) {self.nodeItem.nodeItem.flowName(item.flowName);self.nodeItem.nodeItem.flowId(item.flowId);if(selFlowLayer){layer.close(selFlowLayer);}};}//校验并保存nodeSelf.ok = function (item) {var validateOptions = Validation;validateOptions.validateOptions = true;validateOptions.messages = {_nodeName: '请输入流转名称',_nodeType : '请输入节点类型',flowName : '请选择所属流程'};validateOptions.errorPlacement = function (label, element) {};validateOptions.submitHandler=function (form) {};var $updNodeForm = $('#updNodeForm');$updNodeForm.validate(validateOptions);var is = $updNodeForm.valid();if(!is){layer.alert('信息不完整');return;}$.postAjax(baseUrl+'saveOrUpdFlowNode',ko.mapping.toJS(item.nodeItem),function (state) {if(state.success){layer.alert('保存成功!');closeNodeOpenCommon();nodeTableViewModel.initNodeTable();}else{layer.alert('保存失败!');}});};}// nodeEditDivvar nodeLayer,nodeEditDiv = document.getElementById('nodeEditDiv');/*** 关闭nodeLayer*/function closeNodeOpenCommon() {if(nodeLayer){layer.close(nodeLayer);}}/*** 新增和修改节点信息的公用layer* @param title* @param nodeItemKo*/function nodeOpenCommon(title,nodeItemKo) {nodeLayer = layer.open({title: title,type: 1,content: $(nodeEditDiv),area: ['600px', '250px'],success : function () {ko.cleanNode(nodeEditDiv);ko.applyBindings(nodeItemKo, nodeEditDiv);},end : function () {$(nodeEditDiv).hide();}});}}/*** 修改Link信息* @constructor*/function LinkEditViewModel(linkItem,linkTableViewModel) {var self = this;self.item = ko.mapping.fromJS(linkItem);//选择本节点self.selNode = function () {selNodeCommon(1,self.item,null,'选择本节点');};//选择下一个节点self.selNextNode = function () {selNodeCommon(2,self.item,null,'选择下一个节点');};//取消self.no = function () {closeLinkUpdLayer();};//保存self.ok = function () {var validateOptions = Validation;validateOptions.validateOptions = true;validateOptions.messages = {linkName: '请输入流转名称',conditionStatus : '请输入条件状态',_nodeName : '请选择本节点名称',nextNodeName : '请选择下一个节点名称'};validateOptions.errorPlacement = function (label, element) {};validateOptions.submitHandler=function (form) {};var $updLinkForm = $('#updLinkForm');$updLinkForm.validate(validateOptions);var is = $updLinkForm.valid();if(!is){layer.alert('信息不完整');return;}$.postAjax(baseUrl+'saveOrUpdFlowLink',ko.mapping.toJS(self.item),function (state) {if(state.success){layer.alert('保存成功!');closeLinkUpdLayer();linkTableViewModel.initLinkTable();}else{layer.alert('保存失败!');}});};//关闭layer弹出框function closeLinkUpdLayer() {if(linkUpdLayer){layer.close(linkUpdLayer);}}}/*** 鼠标移入事件*/function trMouseOver() {var $target = $(event.target);if($target.is('td')){$target = $target.parent();}$target.css('background-color','#CCFF80');$target.css('cursor','pointer');}/*** 鼠标移除事件*/function trMouseOut() {var $target = $(event.target);if($target.is('td')){$target = $target.parent();}$target.css('background-color','white');$target.css('cursor','pointer');}/*** 流程节点* @param t* @param linkItemKo* @param flowItemKo* @param title*/function selNodeCommon(t,linkItemKo,flowItemKo,title) {var selNode = document.getElementById('selNode'),$nodeTbody = $('#nodeTbody'),nodeTbodyHtml = $nodeTbody.html(),nodeListViewModel = new NodeListViewModel(linkItemKo,flowItemKo,t);selNodeLayer = layer.open({title: title,type: 1,content: $(selNode),area: ['880px', '460px'],success : function () {ko.cleanNode(selNode);ko.applyBindings(nodeListViewModel, selNode);nodeListViewModel.initNodeTable();},end : function () {$nodeTbody.html(nodeTbodyHtml);$(selNode).hide();}});}/*** 查看流程Link信息的ViewModel* @param flowItem* @constructor*/function LinkTableViewModel(flowItem){var self = this;var linkEditDiv = document.getElementById('linkEditDiv'),flowId = $.trim(flowItem.flowId);self.linkItems = ko.observableArray([]);//初始化列表self.initLinkTable = function () {$.postAjax(baseUrl + 'selectLinkByFlowId', {flowId : flowId}, function (data) {self.linkItems(data);});};//新增self.addLink = function(){var newItem = {flowId: flowId,linkName: '', linkDesc: '',nodeId: '', nodeName: '',nextNodeId: '', nextNodeName: '',conditionStatus: ''};var updLinkVm = new LinkEditViewModel(ko.mapping.fromJS(newItem),self);linkOpenCommon('新增连接信息',updLinkVm);};//修改self.updLink = function (item) {var updLinkVm = new LinkEditViewModel(item,self);linkOpenCommon('修改连接信息',updLinkVm);};//删除self.delLink = function (item) {if($.isEmpty(item.linkId)){layer.alert('发生错误!');return;}var con = layer.confirm('确定删除该记录吗?', {btn: ['确定','取消'] //按钮}, function(){layer.close(con);$.postAjax(baseUrl+'delLink',{linkId : item.linkId},function (state) {if(state.success){layer.msg('删除成功!');self.initLinkTable();}else{layer.alert('删除失败!失败原因:' + state.message);}});}, function(){});};/*** link的layer弹出框公用部分* @param title layer弹出框的标题* @param updLinkVm*/function linkOpenCommon(title,updLinkVm) {linkUpdLayer = layer.open({title: title,type: 1,content: $(linkEditDiv),area: ['600px', '250px'],success : function () {ko.cleanNode(linkEditDiv);//取消绑定ko.applyBindings(updLinkVm, linkEditDiv);//绑定},end :function () {$(linkEditDiv).hide();}});}}/*** 列表页的ViewModel* @constructor*/function TableViewModel() {var self = this,flowEditDiv = document.getElementById('flowEditDiv');self.items = ko.observableArray([]);self.queryFlowName = ko.observable('');self.delFlow = function (item) {if($.isEmpty(item.flowId)){layer.alert('发生错误!');return;}//TODO:注意,此处尚未做级联删除var con = layer.confirm('如果删除该记录,该记录下的节点信息将同时删除.<br>确定删除该记录吗?', {btn: ['确定','取消'] //按钮}, function(){layer.close(con);$.postAjax(baseUrl+'delFlow',{flowId : item.flowId},function (state) {if(state.success){layer.msg('删除成功!');self.init();}else{layer.alert('删除失败!失败原因:' + state.message);}});}, function(){});};//新增self.addFlow = function () {var addVm = ko.mapping.fromJS({flowName:'',flowDesc:''});flowOpenCommon(flowEditDiv,addVm,'新增工作流信息');};//修改self.updFlow = function (item) {var updVm = ko.mapping.fromJS(item);flowOpenCommon(flowEditDiv,updVm,'修改工作流信息');};function flowOpenCommon(flowEditDiv,updVm,title) {flowUpdLayer = layer.open({title: title,type: 1,content: $(flowEditDiv),area: ['320px', '215px'],success:function () {ko.cleanNode(flowEditDiv);//取消绑定var flowEditViewModel = new FlowEditViewModel(updVm);ko.applyBindings(flowEditViewModel, flowEditDiv);//绑定},end:function () {$(flowEditDiv).hide();}});}//初始化列表self.init = function () {$.postAjax(baseUrl + 'queryFlow', {flowName: $.trim(self.queryFlowName())}, function (data) {flowList = data;self.items(data);});};//流程节点self.opeFlowNode = function (item) {selNodeCommon(null,null,ko.mapping.fromJS(item),'流程节点信息');};function FlowEditViewModel(flowItemKo) {var flowSelf = this;flowSelf.flowItem = flowItemKo;flowSelf.ok = function () {var validateOptions = Validation;validateOptions.validateOptions = true;validateOptions.messages = {flowName: '请输入工作流名称'};validateOptions.errorPlacement = function (label, element) {};validateOptions.submitHandler=function (form) {};var $updFlowForm = $('#updFlowForm');$updFlowForm.validate(validateOptions);var is = $updFlowForm.valid();if(!is){layer.alert('信息不完整');return;}$.postAjax(baseUrl+'saveOrUpdFlow',ko.mapping.toJS(flowSelf.flowItem),function (state) {if(state.success){layer.alert('保存成功!');closeFlowLayer();self.init();}else{layer.alert('保存失败!');}});};flowSelf.no = function () {closeFlowLayer();};function closeFlowLayer() {if(flowUpdLayer){layer.close(flowUpdLayer);}}}//流程连接self.opeFlowLink = function (item) {//将tbody缓存起来,解决数据重复问题var $linkTbody = $('#linkTbody'),tbody = $linkTbody.html(),flowLinkDiv = document.getElementById('flowLinkDiv');linkUpdLayer = layer.open({title: '工作流连接信息',type: 1,content: $(flowLinkDiv),area: ['880px', '450px'],success : function(){var linkTableViewModel = new LinkTableViewModel(item);ko.cleanNode(flowLinkDiv);//取消绑定ko.applyBindings(linkTableViewModel, flowLinkDiv);linkTableViewModel.initLinkTable();},end:function () {$(flowLinkDiv).hide();$linkTbody.html(tbody);}});}
})();

关键代码说明

/*** 声明一个TableViewModel*/function TableViewModel() {var self = this,flowEditDiv = document.getElementById('flowEditDiv');//监控对象是一个数组self.items = ko.observableArray([]);//监控对象是一个字符串self.queryFlowName = ko.observable('');self.delFlow = function (item) {if($.isEmpty(item.flowId)){layer.alert('发生错误!');return;}var con = layer.confirm('如果删除该记录,该记录下的节点信息将同时删除.<br>确定删除该记录吗?', {btn: ['确定','取消'] //按钮}, function(){layer.close(con);$.postAjax(baseUrl+'delFlow',{flowId : item.flowId},function (state) {if(state.success){layer.msg('删除成功!');self.init();}else{layer.alert('删除失败!失败原因:' + state.message);}});}, function(){});};//新增self.addFlow = function () {//注意这里的ko.mapping.fromJS,刚开始的时候,我们不是提到了一个ko.mapping.js吗?这里就是其中的一个功能//ko监控的对象结构如下://{key:ko.observable(value)}//也就是需要ko.observable声明一下,如果需要监控的对象键值对比较多,一个个转就太麻烦了//使用ko.mapping.fromJS就可以直接转换了//另外,ko.mapping.toJS则可以反过来将一个ko监控对象转为正常的json对象var addVm = ko.mapping.fromJS({flowName:'',flowDesc:''});flowOpenCommon(flowEditDiv,addVm,'新增工作流信息');};//修改self.updFlow = function (item) {var updVm = ko.mapping.fromJS(item);flowOpenCommon(flowEditDiv,updVm,'修改工作流信息');};function flowOpenCommon(flowEditDiv,updVm,title) {flowUpdLayer = layer.open({title: title,type: 1,content: $(flowEditDiv),area: ['320px', '215px'],success:function () {ko.cleanNode(flowEditDiv);//取消绑定var flowEditViewModel = new FlowEditViewModel(updVm);ko.applyBindings(flowEditViewModel, flowEditDiv);//绑定},end:function () {$(flowEditDiv).hide();}});}//初始化列表self.init = function () {$.postAjax(baseUrl + 'queryFlow', {flowName: $.trim(self.queryFlowName())}, function (data) {flowList = data;//给ko监控对象赋值,不能使用=,//因为ko监控对象的结构并不是简单的json对象格式//需要使用()赋值,如下self.items(data);});};//流程节点self.opeFlowNode = function (item) {selNodeCommon(null,null,ko.mapping.fromJS(item),'流程节点信息');};function FlowEditViewModel(flowItemKo) {var flowSelf = this;flowSelf.flowItem = flowItemKo;flowSelf.ok = function () {var validateOptions = Validation;validateOptions.validateOptions = true;validateOptions.messages = {flowName: '请输入工作流名称'};validateOptions.errorPlacement = function (label, element) {};validateOptions.submitHandler=function (form) {};var $updFlowForm = $('#updFlowForm');$updFlowForm.validate(validateOptions);var is = $updFlowForm.valid();if(!is){layer.alert('信息不完整');return;}$.postAjax(baseUrl+'saveOrUpdFlow',ko.mapping.toJS(flowSelf.flowItem),function (state) {if(state.success){layer.alert('保存成功!');closeFlowLayer();self.init();}else{layer.alert('保存失败!');}});};flowSelf.no = function () {closeFlowLayer();};function closeFlowLayer() {if(flowUpdLayer){layer.close(flowUpdLayer);}}}//流程连接self.opeFlowLink = function (item) {//将tbody缓存起来,解决数据重复问题var $linkTbody = $('#linkTbody'),tbody = $linkTbody.html(),flowLinkDiv = document.getElementById('flowLinkDiv');linkUpdLayer = layer.open({title: '工作流连接信息',type: 1,content: $(flowLinkDiv),area: ['880px', '450px'],success : function(){var linkTableViewModel = new LinkTableViewModel(item);ko.cleanNode(flowLinkDiv);//取消绑定ko.applyBindings(linkTableViewModel, flowLinkDiv);linkTableViewModel.initLinkTable();},end:function () {$(flowLinkDiv).hide();$linkTbody.html(tbody);}});}
//上边提到的$root指的既是这里new出来的TableViewModel
//$('#sysFlowManagePage')[0]要绑定的元素,也是TableViewModel的作用域,也就是说在$('#sysFlowManagePage')[0])中出现的data-bind归TableViewModel管
//注意,不能重复绑定,如果需要清除,使用ko.cleanNode($('#sysFlowManagePage')[0]);清除绑定
$(function () {var vm = new TableViewModel();ko.applyBindings(vm, $('#sysFlowManagePage')[0]);vm.init();});
6.后端接口
结构
比较简单,查出需要的数据返回前端,前端需要保存删除的数据后端删除等
7.注意点
ko监控对象的结构不再是简单的json结构,这也是引入ko.mapping的原因
注意重复绑定的问题,如果ko.applyBindings没有指定绑定作用域的话,默认是当前的document

knockoutJs在项目中的使用相关推荐

  1. canvas java 上传截图_在Vue项目中使用html2canvas生成页面截图并上传

    使用方法 项目中引入 npm install html2canvas html代码 //html代码 js代码 // 引入html2canvas import html2canvas from 'ht ...

  2. android studio 自动提示jni代码,如何将JNI(C/C++本机代码)添加到现有的Android Studio项目中...

    从现有项目中执行以下步骤: 1.修改build.gradle(模块应用程序)看起来像这样(很多变化!): apply plugin: 'com.android.model.application' m ...

  3. android使用webview上传文件,Android项目中如何在webview页面中上传文件

    Android项目中如何在webview页面中上传文件 发布时间:2020-11-26 15:56:27 来源:亿速云 阅读:68 作者:Leah 本篇文章为大家展示了Android项目中如何在web ...

  4. java fragment_Java Web Fragment在项目中使用方法详解

    Web Fragment 是什么 - 它是在 servlet 3.0开始支持的,可以把一个dy web项目拆分为多个项目,解耦合,使其在项目中开发效率提高,下面我演示简单的项目创建过程 用eclips ...

  5. JAVA Web项目中所出现错误及解决方式合集(不断更新中)

    JAVA Web项目中所出现错误及解决方式合集 前言 一.几个或许会用到的软件下载官网 二.Eclipse的[preferences]下没有[sever]选项 三.Tomcat的安装路径找不到 四.T ...

  6. Android Studio 在项目中引用第三方jar包

    在Android Studio项目中引用第三方jar包的方法: 步骤: 1.在build.gradle文件中添加如下代码: 备注:要添加在Android作用域下 sourceSets {main {j ...

  7. Database项目中关于Procedure sp_refreshsqlmodule_internal的错误

    最近项目中发现一怪问题,使用DB项目发布数据库时,总提示 "(110,1): SQL72014: .Net SqlClient Data Provider: Msg 1222, Level ...

  8. 分享.NET开发中经常使用到的代码片段 完全从实际项目中提取出来,也可被反反复复的重复借用...

    几年前,一篇<ASP.NET开发人员经常使用的三十三种代码>非常流行,它总结了一些经常在ASP.NET开发中使用到的代码,直接可以拿来使用.今天重读这篇文章,有感而发,善于总结也是进步,于 ...

  9. C++项目中的extern C {}

    2010-07-10 19:45 by 吴秦, 92864 阅读, 22 评论, 收藏, 编辑 引言 在用C++的项目源码中,经常会不可避免的会看到下面的代码: ? 1 2 3 4 5 6 7 8 9 ...

最新文章

  1. linux文件系统简介
  2. java 不支持fork_为什么Java forkbomb不会导致StackOverflowError?
  3. 项目管理中的客户需求变更时需求分析和解决方法
  4. Linux 下安装 Oracle9i
  5. 【牛客 - 283F】出装方案(最小费用最大流)
  6. HNU 实验七 字符串 E-mail地址
  7. 本地修改PHP修改文件,PHP脚本批量修改本地文件名
  8. Qlikview Session Recovery
  9. Docker容器图形界面显示(运行GUI软件)的配置方法
  10. opencv打开双目,采集标定双目的图片
  11. 在线教学试卷讲评利器——屏幕画笔
  12. Windows系统查询硬盘序列号
  13. termux python turtle库_Python之Turtle库
  14. Docker-ce在线安装
  15. 头条 上传图片大小_遇到不会注册今日头条号,这么办?
  16. 高性能的计算机至少有几个cpu,感觉PC对CPU的性能需求快到极限了
  17. 机器人主流编程语言盘点 及优缺点分析
  18. 计算机开机后无法正常显示桌面图标,电脑开机后不显示桌面图标怎么办
  19. 添加五笔输入法(默认的)windows sever 2012 r2
  20. 数据库sql中S P J SPJ 表的创建

热门文章

  1. python爬虫之 爬取案例网页ajax请求的数据
  2. 基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(八)使用domoticz+mosquitto+Android客户端实现控制mini2440上的LED(一)
  3. 设计模式常用的七大原则
  4. 你见过最水的程序员是啥样的?
  5. iText实现HTML页面导出PDF
  6. html页面导出pdf截断问题,利用wkhtmltopdf(thead)将网页导出为pdf方法;以及存在表格图片被分页打断的问题解决方法...
  7. 新闻发布系统——模糊查询分页与主题分页
  8. 如何让图片跟随div大小自动填满
  9. Caliburn.Micro将枚举 绑定到ComboBox
  10. java -p_javap使用实例图解