首页 > 代码库 > 前端开发框架Bootstrap和KnockoutJ

前端开发框架Bootstrap和KnockoutJ

江湖中那场异常惨烈的厮杀,如今都快被人遗忘了。当年,所有的武林同道为了同一个敌人都拼尽了全力,为数不多的幸存者心灰意冷,隐姓埋名,远赴他乡,他们将唯一的希望寄托给时间。少年子弟江湖老,红颜少女的鬓边也有了白发。多年以后,听闻那个魔头也不久于人世,他们欣欣然回乡,却发现当初殚精竭虑研究出来对付敌人的招数全无用处,曾经受人尊敬的大侠现在被称为——新手 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="http://www.mamicode.com/@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="http://www.mamicode.com/@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 bindingsko.applyBindings(window.adApp.authManageViewModel);

window.adApp.authManageViewModel就是ViewModel,它包含了两个属性(UserList为用户集合,error为提示信息,准确的命名应该类似msg,懒得改了)和若干函数(和服务端交互)。ko.applyBindings将该ViewModel绑定到页面。上述代码还涉及到两个类型:

function NavItem(data) {    var self = this;    data = http://www.mamicode.com/data || {};"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="http://www.mamicode.com/@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="http://www.mamicode.com/@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:本来想写详细点,结果发现写一大堆还不如几行代码来的清楚。文中若有错误之处,请及时告知,大家交流,共同进步。

 

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

 
 
标签: HTML5, CSS3, javascript
绿色通道: 好文要顶 关注我 收藏该文与我联系 
莱布尼茨
关注 - 7
粉丝 - 53
 
 
+加关注
7
0
 
(请您对文章做出评价)
 
« 上一篇:ASP.NET Web API之消息[拦截]处理
» 下一篇:使用DotNetOpenAuth搭建OAuth2.0授权框架
posted on 2013-09-22 14:40 莱布尼茨 阅读(3888) 评论(13) 编辑 收藏

 

发表评论
 
  回复引用
#1楼 2013-09-22 15:24 | 为乐而来  
bootstrap真心不错,一直在用
支持(0)反对(0)
  回复引用
#2楼 2013-09-22 16:42 | Jason.zhou  
一直在用bootstrap,非常不错!
强烈推荐!
支持(0)反对(0)
  回复引用
#3楼 2013-09-22 17:51 | HackerVirus  
good!! 非常高级!!
支持(0)反对(0)
  回复引用
#4楼 2013-09-22 19:35 | 茗::流  
不错
支持(0)反对(0)
  回复引用
#5楼 2013-09-22 20:25 | 茗::流  
还没有时间学习
支持(0)反对(0)
  回复引用
#6楼 2013-09-22 23:02 | 艾斯泽  
我是学习使用jquery-easyui好呢,还是bootstrap好呢?? 
貌似俩个没有可比性。。
支持(0)反对(0)
  回复引用
#7楼[楼主] 2013-09-23 04:29 | 莱布尼茨  
@艾斯泽
前者侧重组件和相关API的使用,后者也有组件,但侧重样式展现。
支持(0)反对(0)
  回复引用
#8楼 2013-09-23 10:11 | wolf_and_wolf  
好文,mark
支持(0)反对(0)
  回复引用
#9楼 2013-09-23 10:53 | 祥子v5  
正在接触这些~~~
支持(0)反对(0)
  回复引用
#10楼 2013-09-24 09:07 | 绿箭侠  
bootstrap 对 ie 浏览器支持不怎么好。。
我想问一下博主,用bootstrap开发的软件,不是有浏览器上的要求么?用户可不想换他们的浏览器 ,请问博主怎么解决呢?
支持(0)反对(0)
  回复引用
#11楼 2013-09-24 13:22 | 墨尔  
我也在学习knockoutjs, 博主文章不错。
支持(0)反对(0)
  回复引用
#12楼[楼主] 2013-09-25 09:55 | 莱布尼茨  
@绿箭侠
个人认为,bootstrap对浏览器的支持做的算好的了。官网上有对浏览器支持的表述。一般网站用用足够了。若是大型商用网站,也许需要在某些方面进行额外处理。也能对这个开源项目做进一步的完善和贡献。
支持(0)反对(0)
  回复引用
#13楼 2014-05-23 16:16 | HMQ  
@发图不发种,菊花万人捅