In the previous article, I introduced to you the combination of BootstrapTable and KnockoutJS to implement the addition, deletion, modification and search functions [1], and introduced some basic usages of knockout.js. Next, we will continue to introduce it to you through this article. If you plan to use ko to do projects, let’s take a look!
Bootstrap is a front-end framework, a good thing for freeing web developers. It shows that the UI is very high-end, atmospheric and high-end. In theory, you don’t need to write a line of css. Just add the appropriate attributes to the tag.
KnockoutJS is a JavaScript-implemented MVVM framework. Very good. For example, after adding or decreasing the list data items, there is no need to refresh the entire control fragment or write JS addition and deletion nodes by yourself. Just define the template and attributes that meet its syntax definitions. Simply put, we only need to pay attention to the access to data.
1. Preview of the effect
In fact, it has no effect. It is just a simple addition, deletion, modification and search. The key is still on the code. Using ko can save a lot of interface DOM data binding operations. Below is the js code for the entire logic of adding, deleting, modifying and searching:
Page effect:
2. Code examples
OK, let's get to the point! The blogger plans to introduce it in two parts. The first part is the table initialization part, and the second part is the button operation addition, deletion and modification part.
1. Table initialization
1.1. Preparation
First look at the js and css files that need to be referenced
<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>
They are all commonly used css and js files. We mainly have two custom js files: knockout.bootstraptable.js and Department.js. In the previous article, we introduced that using ko can customize our data-bind. Similarly, for the binding of table, we also define a custom binding, code inside knockout.bootstraptable.js.
//Add ko custom binding ko.bindingHandlers.myBootstrapTable = {init: function (element, valueAccessor, allBindingsAccessor, viewModel) {//The oParam here is the bound viewmodelvar oViewModel = valueAccessor();var $ele = $(element).bootstrapTable(oViewModel.params);//Add bootstrapTable method to viewmodel oViewModel.bootstrapTable = function () {return $ele.bootstrapTable.apply($ele, arguments);}},update: function (element, valueAccessor, allBindingsAccessor, viewModel) {}};//Initialization (function ($) {//Add a bootstrapTableViewModel method to ko.bootstrapTableViewModel = function (options) {var that = this;this.default = {search: true, //Whether to display table search? This search is a client search and will not enter the server. Therefore, I personally feel that it is meaningless strictSearch: true, showColumns: true, //Whether to display all columns cache:false, showRefresh: true, //Does the refresh button be displayed minimumCountColumns: 2, //The minimum number of columns allowed clickToSelect: true, //Where to enable click select row showToggle: true,}; this.params = $.extend({}, this.default, options || {});//Get the selected record this.getSelections = function () {var arrRes = that.bootstrapTable("getSelections")return arrRes;};//Refresh this.refresh = function () {that.bootstrapTable("refresh");};};})(jQuery);Code doubt: This js file mainly does two things
1. Customize the data-bind property myBootstrapTable. For the update method in ko.bindingHandlers.myBootstrapTable, it is not necessary to define it.
2. Encapsulate the bootstrapTable by adding a bootstrapTableViewModel to the ko object.
1.2. Start binding of html tag
<table id="tb_dept" data-bind="myBootstrapTable:$root"><tr><th data-checkbox="true"></th><th data-field="Name">Department name</th><th data-field="Level">Department level</th><th data-field="Des">Description</th><th data-field="strCreatetime">Create time</th></tr></teaad></table>
Code doubt: Define a table tag and use custom binding to myBootstrapTable. As mentioned in the previous article, $root can be understood as the meaning of initialization. For simplicity, all the columns are written directly in <th>.
1.3. Activate the binding of ko
After the page loads, start ko's binding:
//Initialize $(function () {//1, Init table tableInit.Init();//2, Register the add-on, deletion and modification event operating.operateInit();});//Initialize table var tableInit = {Init: function () {//Binding table viewmodelthis.myViewModel = new ko.bootstrapTableViewModel({url: '/Department/GetDepartment', //Request URL (*) method: 'get', //Request method (*) toolbar: '#toolbar', //Which container is used for tool button queryParams: function (param) {return { limit: param.limit, offset: param.offset };},//Pagination parameters (*) pagination: true, //Does the page display (*) sidePagination: "server", //Pagination method: client client pagination, server server pagination (*) pageNumber: 1, //Initialize the first page to load, default first page pageSize: 10, //Number of record lines per page (*) pageList: [10, 25, 50, 100], //Number of rows per page to select (*)});ko.applyBindings(this.myViewModel, document.getElementById("tb_dept"));}};Code doubt: After the page is loaded, call the bootstrapTableViewModel object encapsulated above to merge the passed parameters, and finally activate the binding, and activate this.myViewModel as the bound viewmodel. The debugging code shows that when the ko.applyBindings(this.myViewModel, document.getElementById("tb_dept")); is executed; the custom binding will take effect and the program will enter the init method of the ko.bindingHandlers.myBootstrapTable object to initialize bootstrapTable. Here is a point to be explained:
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {//The oParam here is the bound viewmodelvar oViewModel = valueAccessor();var $ele = $(element).bootstrapTable(oViewModel.params);//Add bootstrapTable method to viewmodel oViewModel.bootstrapTable = function () {return $ele.bootstrapTable.apply($ele, arguments);}}In the init method above, through the second parameter valueAccessor, we get the currently bound viewmodel, which is the object of this.myViewModel above. The blogger thinks this is conducive to your understanding of the logic of custom binding. Basically, when we execute this sentence var $ele = $(element).bootstrapTable(oViewModel.params);, our table initialization is completed. The blogger defines a collection for the corresponding method in the background. For the sake of completeness, I will post it here:
DepartmentController
2. Button operation
The above is to use our custom data-bind through the initialization of bootstrapTable. Let’s experience the “Shuangweiwai” using monitoring attributes using the button operation below.
2.1. View page
First, define our add-on-deletion button on the view page
<div id="toolbar"><button id="btn_add" type="button"><span aria-hidden="true"></span>Add</button><button id="btn_edit" type="button"><span aria-hidden="true"></span>Modify</button><button id="btn_delete" type="button"><span aria-hidden="true"></span>Delete</button></div>
For simplicity, the blogger used a hidden pop-up box to contain newly added and edited text boxes. Of course, generally speaking, you may use partial views here, and there may be an Edit.cshtml in your project, but here the blogger puts these on one page because this is not the focus of the text.
<div id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"><div role="document"><div><div><button type="button" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button><h4 id="myModalLabel">Operation</h4></div><div><label for="txt_departmentname">Department name</label><input type="text" name="txt_departmentname" data-bind="value:Name" id="txt_departmentname" placeholder="department name"></div><div><label for="txt_departmentlevel">Department level</label><input type="text" name="txt_departmentlevel" data-bind="value:Level" id="txt_departmentlevel" placeholder="departmentlevel"></div><div><label for="txt_des">Description</label><input type="text" name="txt_des" data-bind="value:Des" id="txt_des" placeholder="Description"></div></div><div><button type="button" data-dismiss="modal"><span aria-hidden="true"></span>Close</button><button type="button" id="btn_submit" data-dismiss="modal"><span aria-hidden="true"></span>Save</button></div></div></div>
2.2. JS initialization button operation
//Operation var operation = {//Initialize button event 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()};},//Add operationAdd: 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"));});});});});},//Edit operatorUpdate: function () {$('#btn_edit').on("click", function () {$("#myModal").modal().on("shown.bs.modal", function () {var arrrectedData = tableInit.myViewModel.getSelections();if (!operate.operateCheck(arrrrectedData)) { return; }//Convert the selected row of data to viewmodelko.utils.extend(operate.DepartmentModel, ko.mapping.fromJS(arrrrectedData[0]));ko.applyBindings(operate.DepartmentModel, document.getElementById("myModal"));operate.operateSave();}).on('hidden.bs.modal', function () {//Clear the binding when closing the pop-up box (this clear includes clearing the binding and clearing the registration event)ko.cleanNode(document.getElementById("myModal"));});});});});});},//Delete: function () {$('#btn_delete').on("click", function () {var arrrectedData = tableInit.myViewModel.getSelections();$.ajax({url: "/Department/Delete",type: "post",contentType: 'application/json',data: JSON.stringify(arrrectedData),success: function (data, status) {alert(status);//tableInit.myViewModel.refresh();}});});});},//Save data operateSave: function () {$('#btn_submit').on("click", function () {//Fetch the current viewmodelvar oViewModel = operating.DepartmentModel;//Convert Viewmodel to data modelvar 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();}});});});},//Data verification operateCheck:function(arr){if (arr.length <= 0) {alert("Please select at least one row of data");return false;}if (arr.length > 1) {alert("Only edit one row of data");return false;}return true;}}Code skepticism: Tell us about the execution logic here. First, call operator.operateInit(); in the $(function(){}) method. In the operatorInit() method, register the click event of the button on the page, and also define this.DepartmentModel as the newly edited viewmodel. This viewmodel defines the monitoring attributes corresponding to the page elements. Do you still remember some data-binds in the pop-up box hidden above? Yes, the corresponding value value in it corresponds to the monitoring attributes here. After setting the binding, all the monitoring changes in js that lead to this.DepartmentModel will trigger the value of the binding tags on the interface to change. On the contrary, the change in the value value of all tags on the interface will inevitably cause the change in its monitoring attribute values. This is the so-called two-way binding. Let’s take a look at the execution of two-way binding in detail.
2.3. New operations
$('#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"));});});When our interface triggers a new operation, the hidden modal box mentioned above will pop up first. When the modal box is displayed, first define an empty viewmodel, and then call ko.utils.extend(operate.DepartmentModel, oEmptyModel); This sentence will overwrite the global operating.DepartmentModel by the empty viewmodel. The function of the ko.utils.extend() method is similar to the function of $.extend() in jquery. Both the previous objects are merged based on the subsequent objects, and after the merge, the binding is activated using the new viewmodel. After activating the binding, register the click event of the save button. When adding this, a modal box pops up. Since the monitoring attributes in the viewmodel are all empty, the value of the corresponding interface element will also be cleared, so we see this in the new addition:
When the pop-up box is closed, we execute ko.cleanNode(document.getElementById("myModal")); through the closed event. This sentence is very important because for the same dom, ko can only be bound once. If you need to bind again, you need to clear the binding first. In addition, the cleanNode() method will not only clear the binding, but also clear the events registered in the dom. You need to pay attention when using it!
2.4. Editing operations
$('#btn_edit').on("click", function () {$("#myModal").modal().on("shown.bs.modal", function () {var arrrectedData = tableInit.myViewModel.getSelections();if (!operate.operateCheck(arrrrectedData)) { return; }//Convert the selected row of data with data Model to viewmodelko.utils.extend(operate.DepartmentModel, ko.mapping.fromJS(arrrectedData[0]));ko.applyBindings(operate.DepartmentModel, document.getElementById("myModal"));operate.operateSave();}).on('hidden.bs.modal', function () {//Clear the binding when closing the pop-up box (this clear includes clearing the binding and clearing the registration event)ko.cleanNode(document.getElementById("myModal"));});});});;When we trigger the editing operation, the interface still pops up. In the pop-up event of the pop-up box, we take the currently selected row and then check whether a row is selected. It is best to convert ordinary Json objects into viewmodel with monitoring attributes through the sentence ko.mapping.fromJS(arrrectedData[0]). As mentioned in the previous article, this method requires the support of the js file knockout.mapping-latest.js. After the conversion, the viewmodel is still updated through the ko.utils.extend() method and then activate the binding. Since the viewmodel is updated by the data of the currently selected row, the result is:
2.5. Save operation
After adding and editing pop-up boxes, click Save after modifying the relevant information, and the save event will be triggered.
$('#btn_submit').on("click", function () {//Fetch the current viewmodelvar oViewModel = operating.DepartmentModel;//Convert the Viewmodel to data modelvar 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();}});});When the save event is triggered, we first get the viewmodel bound to the page, that is, the operating.DepartmentModel, and then use the ko.toJS() method to convert the viewmodel with monitoring attributes into a Json object with pure data. This method is built-in to ko and does not require other JS support. After obtaining the json object, send an ajax request to add or edit data. This reflects two-way binding well. After the value of all text boxes on the interface changes, the change of operating.DepartmentModel will also be triggered.
2.6. Delete operation
There is nothing to say about the deletion operation and has nothing to do with ko.
3. Summary
The above introduces the joint use of ko and bootstrapTable through a simple addition, deletion, modification and search operation. ko can free you from the DOM and focus on viewmodel. Looking at the entire js code, you can hardly see jquery's val(), text() and other operations to obtain and assign values to the interface dom. Does it look clean and refreshing, and high-end? Of course, this may just be some of the more basic usages of ko. After all, bloggers have only been learning ko for 3 days, and more advanced usages need to be explored. When you get used to it in a while, you will share some of its advanced usages with everyone. If you think this article can help you understand the principle of ko and its usage, you might as well recommend it. The editor is grateful for it here!
The above is the entire content of the combination of BootstrapTable and KnockoutJS to implement the addition, deletion, modification and search function [2]. I hope it will be helpful to everyone!