加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

前端开发框架Bootstrap和KnockoutJS

发布时间:2020-12-17 21:06:29 所属栏目:安全 来源:网络整理
导读:???????? Bootstrap是一个前端框架,解放Web开发者的好东东,展现出的UI非常高端大气上档次,理论上可以不用写一行css。只要在标签中加上合适的属性即可。请参看Bootstrap中文文档,这是3.0版本。 KnockoutJS是一个JavaScript实现的MVVM框架。非常棒。比如列

????????Bootstrap是一个前端框架,解放Web开发者的好东东,展现出的UI非常高端大气上档次,理论上可以不用写一行css。只要在标签中加上合适的属性即可。请参看Bootstrap中文文档,这是3.0版本。

KnockoutJS是一个JavaScript实现的MVVM框架。非常棒。比如列表数据项增减后,不需要重新刷新整个控件片段或自己写JS增删节点,只要预先定义模板和符合其语法定义的属性即可。简单的说,我们只需要关注数据的存取。官网文档。

Bootstrap负责UI,KnockoutJS负责数据绑定,两者相得益彰,Web前端必备利器。

我们来做一个简单的例子展示一下它们的威力。

要搁以前,实现类似功能,可以有两个选择:a)直接操作DOM,够喝一壶,一般喜欢展现技术同学的首选;b)借助各种拉风的重量级JS框架,比如extjs,使用它们的API以减少工作量,不过这些框架的学习曲线也挺扭曲。当然本文所说的两个框架也涉及到各自的JS类库,but,提供给开发人员的使用方式是完全不同的,后者更松散(废话,两个当然比一个松散)、灵活,且是基于特性声明的方式,个人表示相当不错。下面就让我们开始码吧。

首先搭个初步的框架:

<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,如果一个元素使用了相应的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?=?function?(username)?{
????????????this.clearError();
????????????if?(!username)?{
????????????????error("请输入用户名.");
????????????}
????????????else?{
????????????????this._ajaxRequest("post",?'/api/UserAuthority/AddUser',?{?"":?username?},?function?(data)?{
????????????????????if?(!data.IsSucceed)
????????????????????????this.error(data.Message);
????????????????????else?{
????????????????????????var?user?=?new?User(data.Data);
????????????????????????this.userList.unshift(user);
????????????????????}
????????????????});
????????????}
????????},????????deleteUser?=?function?(user)?{
????????????this._ajaxRequest("delete",?'/api/UserAuthority/DeleteUser',?{?"":?user.userid?},?function?(data)?{
????????????????if?(!data.IsSucceed)
????????????????????this.error(data.Message);
????????????????else?{
????????????????????this.userList.remove(user);
????????????????}
????????????});
????????},????????getUsers?=?function?()?{
????????????this._ajaxRequest("get",?'/api/UserAuthority/GetUsers',?null,?function?(data)?{
????????????????this.userList.removeAll();
????????????????for?(var?i?=?0;?i?<?data.length;?i++)?{
????????????????????userList.push(new?User(data[i]));
????????????????}
????????????});
????????},????????selectUser?=?function?(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?},?function?(data)?{
????????????????user.navitems.removeAll();
????????????????for?(var?i?=?0;?i?<?data.length;?i++)?{
????????????????????user.navitems.push(data[i]);
????????????????}
????????????});
????????},????????clearError?=?function?()?{?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;

????function?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:?function?()?{
????????????????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?=?function?()?{
????var?user?=?this;
????window.adApp.authManageViewModel._ajaxRequest(
????????"put",?'/api/UserAuthority/UpdateNavItems?userid='?+?user.userid,?{?"":?user.navitems()},?function?(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:?function?(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:?function?(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:?function?(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就是一个自定义绑定,定义如下:

ko.bindingHandlers.animVisible?=?{
????update:?function?(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,很爽很舒服。

ps:本来想写详细点,结果发现写一大堆还不如几行代码来的清楚。文中若有错误之处,请及时告知,大家交流,共同进步。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读