|
江湖中那場(chǎng)異常慘烈的廝殺,如今都快被人遺忘了。當(dāng)年,所有的武林同道為了同一個(gè)敵人都拼盡了全力,為數(shù)不多的幸存者心灰意冷,隱姓埋名,遠(yuǎn)赴他鄉(xiāng),他們將唯一的希望寄托給時(shí)間。少年子弟江湖老,紅顏少女的鬢邊也有了白發(fā)。多年以后,聽(tīng)聞那個(gè)魔頭也不久于人世,他們欣欣然回鄉(xiāng),卻發(fā)現(xiàn)當(dāng)初殫精竭慮研究出來(lái)對(duì)付敵人的招數(shù)全無(wú)用處,曾經(jīng)受人尊敬的大俠現(xiàn)在被稱為——新手 or 菜鳥(niǎo)。月下小酌,孤獨(dú)的他們對(duì)著夜空舉起酒杯,吼一聲:“走你,IE6!” -----------------------------------------------------割-------------------------------------------------------------------- Bootstrap是一個(gè)前端框架,解放Web開(kāi)發(fā)者的好東東,展現(xiàn)出的UI非常高端大氣上檔次,理論上可以不用寫(xiě)一行css。只要在標(biāo)簽中加上合適的屬性即可。請(qǐng)參看Bootstrap中文文檔,這是3.0版本。 KnockoutJS是一個(gè)JavaScript實(shí)現(xiàn)的MVVM框架。非常棒。比如列表數(shù)據(jù)項(xiàng)增減后,不需要重新刷新整個(gè)控件片段或自己寫(xiě)JS增刪節(jié)點(diǎn),只要預(yù)先定義模板和符合其語(yǔ)法定義的屬性即可。簡(jiǎn)單的說(shuō),我們只需要關(guān)注數(shù)據(jù)的存取。官網(wǎng)文檔。 Bootstrap負(fù)責(zé)UI,KnockoutJS負(fù)責(zé)數(shù)據(jù)綁定,兩者相得益彰,Web前端必備利器。 我們來(lái)做一個(gè)簡(jiǎn)單的例子展示一下它們的威力。
要擱以前,實(shí)現(xiàn)類似功能,可以有兩個(gè)選擇:a)直接操作DOM,夠喝一壺,一般喜歡展現(xiàn)技術(shù)同學(xué)的首選;b)借助各種拉風(fēng)的重量級(jí)JS框架,比如extjs,使用它們的API以減少工作量,不過(guò)這些框架的學(xué)習(xí)曲線也挺扭曲。當(dāng)然本文所說(shuō)的兩個(gè)框架也涉及到各自的JS類庫(kù),but,提供給開(kāi)發(fā)人員的使用方式是完全不同的,后者更松散(廢話,兩個(gè)當(dāng)然比一個(gè)松散)、靈活,且是基于特性聲明的方式,個(gè)人表示相當(dāng)不錯(cuò)。下面就讓我們開(kāi)始碼吧。 首先搭個(gè)初步的框架: <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,如果一個(gè)元素使用了相應(yīng)的class,它就會(huì)呈現(xiàn)bootstrap中預(yù)定義的樣式。bootstrap還提供了data-xxx屬性,這是用來(lái)以聲明方式使用組件,這里沒(méi)有涉及。now,界面如下:
圖中標(biāo)注了需要改進(jìn)的兩個(gè)地方,此時(shí)先不考慮。我們現(xiàn)在要先把數(shù)據(jù)從后臺(tái)取出,以及其它的一些操作,于是引進(jìn)KnockoutJS。接觸過(guò)WPF的都知道ViewModel的概念,說(shuō)白了就是將前端分為UI和交互邏輯,ViewModel就負(fù)責(zé)交互邏輯,knockoutJS也有這個(gè)東西。結(jié)合例子具體來(lái)看: window.adApp.authManageViewModel = (function (ko) { var userList = ko.observableArray(), error = ko.observable(), addUser = function (username) { this.clearError(); if (!username) { error("請(qǐng)輸入用戶名."); } 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為當(dāng)前對(duì)象viewmodel success: callback, error: function () { this.error("服務(wù)器錯(cuò)誤."); } }); } })(ko); // Initiate the Knockout bindings ko.applyBindings(window.adApp.authManageViewModel); window.adApp.authManageViewModel就是ViewModel,它包含了兩個(gè)屬性(UserList為用戶集合,error為提示信息,準(zhǔn)確的命名應(yīng)該類似msg,懶得改了)和若干函數(shù)(和服務(wù)端交互)。ko.applyBindings將該ViewModel綁定到頁(yè)面。上述代碼還涉及到兩個(gè)類型: 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("保存成功!"); } }); } 現(xiàn)在頁(yè)面代碼如下: <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集合有項(xiàng),才顯示該表格,注意if、ifnot的作用范圍不包括table標(biāo)記本身,而是從thead開(kāi)始*@ <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的綁定對(duì)象設(shè)為currentUser,如果currentUser為空,則該div中的內(nèi)容不會(huì)顯示*@ <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屬性,作用不言自明。需要注意的是所謂自定義綁定。當(dāng)綁定的值變動(dòng)了,希望執(zhí)行額外的邏輯(和c#中的事件相似),就會(huì)用到。這里,當(dāng)error的值有變動(dòng),為空時(shí)提示面板隱藏,否則顯示: <div id="divWaring" class="alert alert-warning" style="display: none;" data-bind="animVisible: error"></div> animVisible就是一個(gè)自定義綁定,定義如下: 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,接下來(lái),當(dāng)點(diǎn)擊表格的每一行,currentUser就會(huì)自動(dòng)計(jì)算得到(ko.computed),并反饋到界面,綁定了該字段的div內(nèi)部的相應(yīng)節(jié)點(diǎn)的狀態(tài)也會(huì)相應(yīng)變化(checkbox)。保存啥的就不說(shuō)了。綜上所述,除了必要的與后臺(tái)交互的代碼,涉及到操作UI和DOM節(jié)點(diǎn),我們不需要寫(xiě)一行JS,很爽很舒服。 ps:本來(lái)想寫(xiě)詳細(xì)點(diǎn),結(jié)果發(fā)現(xiàn)寫(xiě)一大堆還不如幾行代碼來(lái)的清楚。文中若有錯(cuò)誤之處,請(qǐng)及時(shí)告知,大家交流,共同進(jìn)步。
轉(zhuǎn)載請(qǐng)注明本文出處:http://www.cnblogs.com/newton/p/3328058.html |
|
|
來(lái)自: ThinkTank_引擎 > 《knockoutjs》