江湖中那场异常惨烈的厮杀,如今都快被人遗忘了。当年,所有的武林同道为了同一个敌人都拼尽了全力,为数不多的幸存者心灰意冷,隐姓埋名,远赴他乡,他们将唯一的希望寄托给时间。少年子弟江湖老,红颜少女的鬓边也有了白发。多年以后,听闻那个魔头也不久于人世,他们欣欣然回乡,却发现当初殚精竭虑研究出来对付敌人的招数全无用处,曾经受人尊敬的大侠现在被称为——新手 or 菜鸟。月下小酌,孤独的他们对着夜空举起酒杯,吼一声:“走你,IE6!”

-----------------------------------------------------割--------------------------------------------------------------------

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


knockoutjs补充:

  1. 若前端元素绑定的是普通属性(即非observable属性),虽然属性值的改变不会反应到前端,但是前端值的改变会改变属性值;也就是说在这两种模式下,后端都会监听前端的相关事件(如input的change事件)
  2. js中直接设置observable属性值,比如this.ItemData.IsChecked(true);,也将触发IsChecked.subscribe订阅的事件,但不会触发前端dom事件,比如checkbox的change事件
  3. 可以属性链绑定,如<span data-bind="text: BasicInfo.RoleName"></span>

转载请注明本文出处:http://www.cnblogs.com/newton/p/3328058.html

前端开发框架Bootstrap和KnockoutJS的更多相关文章

  1. Bootstrap 简洁、直观、强悍的前端开发框架,让web开发更迅速、简单。

    Bootstrap 简洁.直观.强悍的前端开发框架,让web开发更迅速.简单.

  2. Bootstrap非常简单实用的web前端开发框架

    今天无意间用firebug看网站的代码发现了Bootstrap,之前从来没有听说过这个东东,于是对它产生了好奇感,通过百度我了解到了Bootstrap是一款非常简单,强悍,实用,移动设备端优先使用的这 ...

  3. Crumpet – 使用很简单的响应式前端开发框架

    Crumpet 是一个简单的响应式的基于 SASS/SCSS 的响应式前端框架,保持你的 HTML 代码简洁.内置尽量使用占位符选择器,以减少你的 HTML 标记的大小,没有凌乱的 HTML 代码.快 ...

  4. Furatto – 轻量,友好的响应式前端开发框架

    Furatto 是一个基于 Bootstrap & Foundation 的前端开发框架,用于快速开发网站.这个框架采用流行的扁平化设计和响应式设计.除了布局和网格之外,所有的主要元素都有预定 ...

  5. 前端框架——BootStrap学习

    BootStrap简单总结下:1.栅格系统,能够很好的同时适应手机端和PC端(及传说中的响应式布局) 2.兼容性好 接下来是对BootStrap学习的一些基础案例总结和回顾: 首先引入:bootstr ...

  6. 国内HTML5前端开发框架汇总

    国内HTML5前端开发框架汇总 Dawei Cheng 程大伟... 于 星期日, 02/12/2012 - 20:53 提交 国外很有多优秀的HTML5前端开发框架相信大家都耳熟能详:JQuery ...

  7. 2017年当下最值得你关注的前端开发框架,不知道你就OUT了!

    近几年随着 jQuery.Ext 以及 CSS3 的发展,以 Bootstrap 为代表的前端开发框架如雨后春笋般挤入视野,可谓应接不暇. 在这篇分享中,我将总结2017年当下最值得你关注的前端开发框 ...

  8. 前端框架Bootstrap - 快速搭建网站

    Bootstrap简介         Bootstrap是Twitter推出的一个开源的用于前端开发的工具包.是一个CSS/HTML/JavaScript框架.Bootstrap是基于HTML5和C ...

  9. 国外很有多优秀的HTML5前端开发框架

    国外很有多优秀的HTML5前端开发框架 相信大家都耳熟能详:JQuery Mobile,Twitter Bootstrap, Schena Touch,  BackBone等等. 同样,也存在很多国内 ...

随机推荐

  1. Android常见控件— — —ProgressDialog

    package com.example.uiwidgettest2; import android.app.Activity;import android.app.AlertDialog;import ...

  2. iOS 中捕获程序崩溃日志

    iOS 中捕获程序崩溃日志 (2014-04-22 17:35:59) 转载▼     iOS开发中遇到程序崩溃是很正常的事情,如何在程序崩溃时捕获到异常信息并通知开发者,是大多数软件都选择的方法.下 ...

  3. SPOJ BALNUM

    一开始题看错了...dp[pos][sets][viss],其中sets表示出现次数,viss表示出现没有. #include<iostream> #include<cstdio&g ...

  4. Node.js高级编程读书笔记 - 1 基本概念

    Outline 1 概述和安装 1.1 安装Node 1.2 Node简介 2 Node核心API基础 2.1 加载模块 2.2 应用缓冲区处理.编码和解码二进制数据 2.3 使用时间发射器模式简化事 ...

  5. Linux系统下fd分配的方法

    最近几天在公司里写网络通讯的代码比较多,自然就会涉及到IO事件监测方法的问题.我惊奇的发现select轮训的方法在那里居然还大行其道.我告诉他们现在无论在Linux系统下,还是windows系统下,s ...

  6. oracle数据库--启动和关闭

    oracle--启动 oracle数据库的启动过程包含3个步骤:启动实例->加载数据库->打开数据库 分步骤启动过程可以对数据库进行不同的维护操作,对应我们不同的需求. 启动模式: 1.s ...

  7. ssh隧道(端口转发)

    本地转发: ssh -Nf -L [bind_address:]port:host:hostport sshServer -Nf 后台运行 -L 本地转发 [bind_address] 绑定本地地址, ...

  8. 有向图强连通分量 Tarjan算法

    [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极 ...

  9. 解决Maven项目编译时提示:源值1.5已过时,将在未来所有版本中删除

    每次编译项目时,都提示:源值1.5已过时,将在未来所有版本中删除 查了一些资料,发现是因为IDEA默认把项目的源代码版本设置为jdk1.5,目标代码设置为jdk1.5 解决方案:  修改Maven的S ...

  10. ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(十四)之漏掉的客服消息

    前言 不知不觉已经十四篇了,其实已经没有什么可写了.但是突然发现layim中带的客服功能没有用到.于是乎,抽点时间完成吧.其实之前的工作已经把客服功能完成了一大半,剩下的我们稍微调整即可.今天的演示我 ...