BootstrapTable與KnockoutJS相結(jié)合實現(xiàn)增刪改查功能【二】
在上篇文章給大家介紹了BootstrapTable與KnockoutJS相結(jié)合實現(xiàn)增刪改查功能【一】,介紹了下knockout.js的一些基礎用法。接下來通過本文繼續(xù)給大家介紹。如果你也打算用ko去做項目,且看看吧!
Bootstrap是一個前端框架,解放Web開發(fā)者的好東東,展現(xiàn)出的UI非常高端大氣上檔次,理論上可以不用寫一行css。只要在標簽中加上合適的屬性即可。
KnockoutJS是一個JavaScript實現(xiàn)的MVVM框架。非常棒。比如列表數(shù)據(jù)項增減后,不需要重新刷新整個控件片段或自己寫JS增刪節(jié)點,只要預先定義模板和符合其語法定義的屬性即可。簡單的說,我們只需要關注數(shù)據(jù)的存取。
一、效果預覽
其實也沒啥效果,就是簡單的增刪改查,重點還是在代碼上面,使用ko能夠大量節(jié)省界面DOM數(shù)據(jù)綁定的操作。下面是整個整個增刪改查邏輯的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, //最少允許的列數(shù) 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">創(chuàng)建時間</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 }; },//傳遞參數(shù)(*) pagination: true, //是否顯示分頁(*) sidePagination: "server", //分頁方式:client客戶端分頁,server服務端分頁(*) pageNumber: 1, //初始化加載第一頁,默認第一頁 pageSize: 10, //每頁的記錄行數(shù)(*) pageList: [10, 25, 50, 100], //可供選擇的每頁的行數(shù)(*) }); ko.applyBindings(this.myViewModel, document.getElementById("tb_dept")); } };
代碼釋疑:頁面加載完成之后,調(diào)用上面封裝的bootstrapTableViewModel對象合并傳遞的參數(shù),最后激活綁定,將this.myViewModel作為綁定的viewmodel激活。調(diào)試代碼可知,當執(zhí)行到 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方法,通過第二個參數(shù)valueAccessor,我們得到的是當前綁定的viewmodel,也就是我們上面的this.myViewModel這個對象,博主覺得這一點有利于你理解自定義綁定的邏輯?;旧蠄?zhí)行到 var $ele = $(element).bootstrapTable(oViewModel.params); 這一句的時候,我們表格的初始化就完成了。后臺對應的方法博主隨便定義了一個集合,為了完整,這里還是貼出來:
DepartmentController
2、按鈕操作
上面通過bootstrapTable的初始化完成了我們的自定義data-bind的使用。下面的按鈕操作我們來體驗一把使用監(jiān)控屬性的“爽歪歪”。
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">×</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; } //將選中該行數(shù)據(jù)有數(shù)據(jù)Model通過Mapping組件轉(zhuǎn)換為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(); } }); }); }, //保存數(shù)據(jù) operateSave: function () { $('#btn_submit').on("click", function () { //取到當前的viewmodel var oViewModel = operate.DepartmentModel; //將Viewmodel轉(zhuǎn)換為數(shù)據(jù)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(); } }); }); }, //數(shù)據(jù)校驗 operateCheck:function(arr){ if (arr.length <= 0) { alert("請至少選擇一行數(shù)據(jù)"); return false; } if (arr.length > 1) { alert("只能編輯一行數(shù)據(jù)"); return false; } return true; } }
代碼釋疑:說說這里的執(zhí)行邏輯,首先在$(function(){})方法里面調(diào)用 operate.operateInit(); 。在operateInit()方法里面注冊頁面上面按鈕的點擊事件,同時也定義 this.DepartmentModel 作為我們新增編輯的viewmodel,這個viewmodel里面定義了和頁面元素對應的監(jiān)控屬性。還記得上面隱藏的彈出框里面的一些data-bind嗎,沒錯,里面對應的value值就是和這里的監(jiān)控屬性對應,這樣設置綁定之后,js里面所有的導致 this.DepartmentModel 里面監(jiān)控的變化,都會觸發(fā)界面上面這些綁定標簽的value值變化,反之,界面上面的所有標簽的Value值的變化,也勢必會引起它的監(jiān)控屬性值的變化,此之所謂雙向綁定。下面具體看看雙向綁定的執(zhí)行。
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")); }); });
當我們界面觸發(fā)新增操作的時候,首先會彈出上面說的隱藏模態(tài)框。在模態(tài)框顯示的時候,首先定義一個空的viewmodel,然后調(diào)用 ko.utils.extend(operate.DepartmentModel, oEmptyModel); 這一句,將全局的operate.DepartmentModel被空的viewmodel覆蓋。ko.utils.extend()這個方法的作用和jquery里面的$.extend()作用類似,都是根據(jù)后面對象合并前面對象,合并之后,使用新的viewmodel激活綁定。激活綁定之后,注冊保存按鈕的click事件。這樣新增的時候,彈出模態(tài)框,由于viewmodel里面的監(jiān)控屬性都是空的,對應界面元素的value也會被清空,所以新增我們看到是這樣:
當彈出框關閉后,我們通過關閉的事件,執(zhí)行 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; } //將選中該行數(shù)據(jù)有數(shù)據(jù)Model通過Mapping組件轉(zhuǎn)換為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")); }); });
當我們觸發(fā)編輯操作的時候,界面還是彈出框。在彈出框的彈出事件里面,我們?nèi)〉疆斍斑x中的行,然后校驗是否選中了一行。最好通過 ko.mapping.fromJS(arrselectedData[0]) 這一句,將普通的Json對象轉(zhuǎn)換為帶有監(jiān)控屬性的viewmodel,上篇說過,這個方法需要 knockout.mapping-latest.js 這個js文件的支持。轉(zhuǎn)換之后,還是通過ko.utils.extend()方法更新viewmodel,然后激活綁定。由于viewmodel被當前選中行的數(shù)據(jù)更新了,所以得到結(jié)果:
2.5、保存操作
在新增和編輯彈出框之后,修改相關信息后點擊保存,就會觸發(fā)保存事件。
$('#btn_submit').on("click", function () { //取到當前的viewmodel var oViewModel = operate.DepartmentModel; //將Viewmodel轉(zhuǎn)換為數(shù)據(jù)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(); } }); });
當觸發(fā)保存事件的時候,我們首先取到頁面綁定的viewmodel,即operate.DepartmentModel,然后使用ko.toJS()方法將帶有監(jiān)控屬性的viewmodel轉(zhuǎn)換為純數(shù)據(jù)的Json對象,這個方法是ko內(nèi)置的,不需要其他js支持。得到json對象之后,發(fā)送ajax請求,去新增或者編輯數(shù)據(jù)。這樣就很好地體現(xiàn)了雙向綁定,界面上面所有文本框的value發(fā)生了變化之后,也會觸發(fā)operate.DepartmentModel的變化。
2.6、刪除操作
刪除操作沒什么好說的,和ko關系不大。
三、總結(jié)
以上通過一個簡單的增刪改查操作,介紹了下ko和bootstrapTable的聯(lián)合使用。ko可以讓你從DOM中解放出來,把關注點放在viewmodel上面??v觀整個js代碼,幾乎看不到jquery的val()、text()等對界面dom做取值和賦值的操作,是不是看著干凈清爽,并且高大上了呢~~當然,這或許只是ko的一些比較基礎的用法,畢竟博主學習ko才3天,更多高級用法還有待摸索,等過段時間用熟了,再將它的一些高級用法分享給大家。如果你覺得本文能夠幫助你理解ko的原理以及它的一些用法,不妨推薦下,小編在此感激不盡!
以上所述是小編給大家介紹的BootstrapTable與KnockoutJS相結(jié)合實現(xiàn)增刪改查功能【二】的全部內(nèi)容,希望對大家有所幫助!
相關文章
JavaScript中的await函數(shù)使用小結(jié)
async 函數(shù)是 AsyncFunction 構造函數(shù)的實例,并且其中允許使用 await 關鍵字,async 和 await 關鍵字讓我們可以用一種更簡潔的方式寫出基于 Promise 的異步行為,而無需刻意地鏈式調(diào)用 promise,這篇文章主要介紹了JavaScript中的await,需要的朋友可以參考下2024-01-01javascript 函數(shù)聲明與函數(shù)表達式的區(qū)別介紹
javascript中的函數(shù)聲明與函數(shù)表達式使用比較頻繁,可能很多的朋友都不知道他們之間的區(qū)別,在此為大家詳細介紹下,希望對大家有所幫助2013-10-10JS獲取經(jīng)緯度并根據(jù)經(jīng)緯度得到城市信息簡單示例
前端時間剛好使用了百度地圖的js api定位獲取用戶當前經(jīng)緯度并獲取當前詳細位置的功能,為了方便下次找起來方便一些自己在這里記錄一下,這篇文章主要給大家介紹了關于JS獲取經(jīng)緯度并根據(jù)經(jīng)緯度得到城市信息的相關資料,需要的朋友可以參考下2023-11-11Javascript驗證Visa和MasterCard信用卡號的方法
這篇文章主要介紹了Javascript驗證Visa和MasterCard信用卡號的方法,涉及javascript正則驗證的相關技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-07-07PHP實現(xiàn)基于Redis的MessageQueue隊列封裝操作示例
這篇文章主要介紹了PHP實現(xiàn)基于Redis的MessageQueue隊列封裝操作,結(jié)合實例形式分析了Redis的PHP消息隊列封裝與使用相關操作技巧,需要的朋友可以參考下2019-02-02