Bootstrap是@L_674_0@前端框架,解放Web开发者的好东东,展现出的UI非常高端大气上档次,理论上可以不用写一行css。只要在标签中加上合适的属性即可。请参看Bootstrap中文文档,这是3.0版本。
KnockoutJS是@L_674_0@JavaScript实现的MVVM框架。非常棒。比如列表数据项增减后,不需要重新刷新整个控件片段或自己写JS增删节点,只要预先定义模板和符合其语法定义的属性即可。简单的说,我们只需要关注数据的存取。官网文档。
Bootstrap负责UI,KnockoutJS负责数据绑定,两者相得益彰,Web前端必备利器。
要搁以前,实现类似功能,可以有两个选择:a)直接操作DOM,够喝一壶,一般喜欢展现技术同学的首选;b)借助各种拉风的重量级JS框架,比如extjs,使用它们的API以减少工作量,不过这些框架的学习曲线也挺扭曲。当然本文所说的两个框架也涉及到各自的JS类库,but,提供给开发人员的使用方式是完全不同的,后者更松散(废话,两个当然比@L_674_0@松散)、灵活,且是基于特性声明的方式,个人表示相当不错。下面就让我们开始码吧。
首先搭个初步的框架:
<div id="divAuthManage" class="row" style="margin-top: 30px;"> <div class="col-md-4 col-sm-4 col-xs-6"> <div> <div class="input-group"> <span class="input-group-addon">用户名</span> <input id="inputUserName" type="text" class="form-control" /> <span class="btn btn-priMary input-group-btn">添加</span> </div> <div id="divWaring" class="alert alert-warning" style="display: none;"></div> </div> <table class="table table-bordered table-hover" style="margin-top: 20px;"> <thead> <tr> <th>用户ID</th> <th>用户名</th> <th style="text-align: center;">删除</th> </tr> </thead> <tbody> </tbody> </table> </div> <div class="col-md-8 col-sm-8 col-xs-12"> @foreach (AreaElement area in Model.Areas) { <div class="panel panel-default"> <div class="panel-heading"> @{if (area.Sites.Count == 0) { <label class="checkBox"> <input type="checkBox" value="@area.Code" /> @area.Name </label> } else { @area.Name } } </div> @if (area.Sites.Count > 0) { <div class="panel-body"> @foreach (SiteElement site in area.Sites) { <label class="checkBox-inline"> <input type="checkBox" value="@site.Code" />@site.Name </label> } </div> } </div> } <p class="text-right"> <button type="button" class="btn btn-default">保存</button> </p> </div> </div>
这里就用到了bootstrap,如果@L_674_0@元素使用了相应的class,它就会呈现bootstrap中预定义的样式。bootstrap还提供了data-xxx属性,这是用来以声明方式使用组件,这里没有涉及。Now,界面如下:
图中标注了需要改进的两个地方,此时先不考虑。我们现在要先把数据从后台取出,以及其它的一些操作,于是引进KnockoutJS。接触过WPF的都知道viewmodel的概念,说白了就是将前端分为UI和交互逻辑,viewmodel就负责交互逻辑,knockoutJS也有这个东西。结合例子具体来看:
window.adApp.authManageviewmodel = (function (ko) { var userList = ko.observableArray(), error = ko.observable(), addUser =@R_472_3816@ (userName) { this.clearError(); if (!userName) { error("请输入用户名."); } else { this._ajaxrequest("post", '/api/UserAuthority/AddUser', { "": username },@R_472_3816@ (data) { if (!data.IsSucceed) this.error(data.messagE); else { var user = new User(data.Data); thiS.UserList.unshift(user); } }); } }, deleteUser =@R_472_3816@ (user) { this._ajaxrequest("delete", '/api/UserAuthority/deleteUser', { "": user.userid },@R_472_3816@ (data) { if (!data.IsSucceed) this.error(data.messagE); else { thiS.UserList.remove(user); } }); }, getUsers =@R_472_3816@ () { this._ajaxrequest("get", '/api/UserAuthority/GetUsers', null,@R_472_3816@ (data) { thiS.UserList.removeAll(); for (var i = 0; i < data.length; i++) { userList.push(new User(data[i])); } }); }, SELEctUser =@R_472_3816@ (user) { for (var i = 0; i < userList().length; i++) { userList()[i].SELEcted(false); } user.SELEcted(true); this._ajaxrequest("get", '/api/UserAuthority/GetAccessNavItems', { userid: user.userid },@R_472_3816@ (data) { user.navitems.removeAll(); for (var i = 0; i < data.length; i++) { user.navitems.push(data[i]); } }); }, clearError =@R_472_3816@ () { error(""); }; var viewmodel = { userList: userList, error: error, _ajaxrequest: ajaxrequest, addUser: addUser, deleteUser: deleteUser, clearError: clearError, _getUsers: getUsers, SELEctUser: SELEctUser, currentUser: ko.computed(function () { for (var i = 0; i < userList().length; i++) { if (userList()[i].SELEcted()) { return userList()[i]; } } return null; }) }; viewmodel._getUsers(); return viewmodel; @R_472_3816@ ajaxrequest(type, url, data, callBACk) { // Ajax Helper this.clearError(); $.ajax({ url: url, data: data, type: type, dataType: "json", context: this,//指定this为当前对象viewmodel success: callBACk, error:@R_472_3816@ () { this.error("服务器错误."); } }); } })(ko); // Initiate the Knockout bindings ko.applyBindings(window.adApp.authManageviewmodel);
window.adApp.authManageviewmodel就是viewmodel,它包含了两个属性(UserList为用户集合,error为提示信息,准确的命名应该类似msg,懒得改了)和若干函数(和服务端交互)。ko.applyBindings将该viewmodel绑定到页面。上述代码还涉及到两个类型:
function NavItem(data) { var self = this; data = data || {}; // Persisted properties self.code = data.code; self.name = data.name; } function User(data) { var self = this; data = data || {}; // Persisted properties self.userid = data.userid; self.username = data.username; data.navitems = data.navitems || []; self.navitems = ko.observableArray(data.navitems); self.SELEcted = ko.observable(false); } User.prototype.updateNavs =@R_472_3816@ () { var user = this; window.adApp.authManageviewmodel._ajaxrequest( "put", '/api/UserAuthority/updateNavItems?userid=' + user.userid, { "": user.navitems()},@R_472_3816@ (data) { if (!data.IsSucceed) this.error(data.messagE); else { this.error("保存成功!"); } }); }
<div id="divAuthManage" class="row" style="margin-top: 30px;"> <div class="col-md-4 col-sm-4 col-xs-6"> <div> <div class="input-group"> <span class="input-group-addon">用户名</span> @*data-bind="input: clearError" 不支持input绑定,so换用自定义绑定,or采用event绑定如下*@ <input id="inputUserName" type="text" class="form-control" data-bind="event: { input: clearError }" /> <span class="btn btn-priMary input-group-btn" data-bind="click:@R_472_3816@ (data, event) { addUser(inputUserName.value) }">添加</span> </div> <div id="divWaring" class="alert alert-warning" style="display: none;" data-bind="animVisible: error"></div> </div> @*如果userList集合有项,才显示该表格,注意if、ifnot的作用范围不包括table标记本身,而是从thead开始*@ <table data-bind="if: userList().length > 0" class="table table-bordered table-hover" style="margin-top: 20px;"> <thead> <tr> <th>用户ID</th> <th>用户名</th> <th style="text-align: center;">删除</th> </tr> </thead> <tbody data-bind="foreach: userList"> <tr data-bind="css: { success: SELEcted }, click:@R_472_3816@ (data, event) { $parent.SELEctUser($data) }"> <td><span data-bind="text: userid"></span></td> <td><span data-bind="text: username"></span></td> <td style="text-align: center;"> <button type="button" class="btn btn-default btn-xs" data-bind="click:@R_472_3816@ (data, event) { $parent.deleteUser($data) }, clickBubble: false"> <span class="glyphicon glyphicon-remove"></span> </button> </td> </tr> </tbody> </table> </div> @*将下面div的绑定对象设为currentUser,如果currentUser为空,则该div中的内容不会显示*@ <div class="col-md-8 col-sm-8 col-xs-12" data-bind="with: currentUser"> @foreach (AreaElement area in Model.Areas) { <div class="panel panel-default"> <div class="panel-heading"> @{if (area.Sites.Count == 0) { <label class="checkBox"> <input type="checkBox" value="@area.Code" data-bind="checked: navitems" /> @area.Name </label> } else { @area.Name } } </div> @if (area.Sites.Count > 0) { <div class="panel-body"> @foreach (SiteElement site in area.Sites) { <label class="checkBox-inline"> <input type="checkBox" value="@site.Code" data-bind="checked: navitems" />@site.Name </label> } </div> } </div> } <p class="text-right"> <button type="button" class="btn btn-default" data-bind="click: UPDATENavs">保存</button> </p> </div> </div>
代码中加了很多data-bind属性,作用不言自明。需要注意的是所谓自定义绑定。当绑定的值变动了,希望执行额外的逻辑(和c#中的事件相似),就会用到。这里,当error的值有变动,为空时提示面板隐藏,否则显示:
<div id="divWaring" class="alert alert-warning" style="display: none;" data-bind="animVisible: error"></div>
animVisible就是@L_674_0@自定义绑定,定义如下:
ko.bindingHandlers.animVisible = { UPDATE:@R_472_3816@ (elem, valueAccessor) { var error = ko.unwrap(valueAccessor()); if (error) { elem.innerText = error; $(elem).show(300); } else { $(elem).hide(300); elem.innerText = ""; } } };
OK,接下来,当点击表格的每一行,currentUser就会自动计算得到(ko.computed),并反馈到界面,绑定了该字段的div内部的相应节点的状态也会相应变化(checkBox)。保存啥的就不说了。综上所述,除了必要的与后台交互的代码,涉及到操作UI和DOM节点,我们不需要写一行JS,很爽很舒服。
以上是大佬教程为你收集整理的前端开发框架Bootstrap和KnockoutJS全部内容,希望文章能够帮你解决前端开发框架Bootstrap和KnockoutJS所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。