初識AngularJS
在使用了AngularJS重構團隊內部的平臺之後,一直想總結點什麼,這裡先說說學習和使用AngularJS的感受。AngularJS是一款開源的JavaScript MV*(MVW、MVVM、MVC)框架,目前由Google維護。AngularJS彌補了HTML在構建應用方面的不足,其通過使用識別符號(directives)結構,來擴充套件Web應用中的HTML詞彙,使開發者可以使用HTML來宣告動態內容,從而使得Web開發和測試工作變得更加容易。
AngularJS的創始人Misko是這樣來描述框架誕生的歷史的:AngularJS最初是作為一個編外專案(side project),當時我想去看看是否有可能讓Web設計師(非開發者)只使用HTML標籤來建立簡單的應用程式。隨著時間的推移,AngularJS演變成了一個全面的開發框架。AngularJS的設計理念是:構建UI應該是宣告式的,這樣的靈感來自於Misko在Adobe公司從事Flex方面工作的時候。
談談我個人使用的感受吧:最大的區別是對開發流程的影響,以往我想做一個Web應用,會先用純Html構造原型,所有的資料均是假資料,靜態的寫到Html檔案中,然後不斷的除錯CSS樣式。待頁面整體的展示讓我滿意之後,就開始Js與伺服器的互動開發,並初步把頁面的假資料替換掉,繫結成Js呼叫Ajax的返回。然而在使用到Table / List等資料載入的時候,我通常會在Js中來構造這些Dom元素。隨著應用的龐大和複雜,我發現我在Js中構造了大量的Dom元素,下面是一個例子:
function buildPersonHtml(data){
var html = [];
data = data[0];
for(var id in data){
var count = 0;
if(data[id].length==0)
continue;
html[html.length] = '<div class="person alert alert-success">';
html[html.length] = '<span><i class="icon-user"></i> '+id+' 的任務列表:</span>';
html[html.length] = '<table class="table table-bordered">';
html[html.length] = '<thead><tr><th>時間</th><th>功能點</th><th>詳細情況</th><th>完成進度</th><th>耗時</th><th>備註</th>';
html[html.length] = '</tr></thead><tbody>';
orderSubTask(data[id]);
for(var i=0; i<data[id].length;i++){
var subtask = data[id][i];
if(subtask.progress != '100%')
count ++;
html[html.length] = '<tr>';
if(subtask.status=='end')
html[html.length] = '<td>開始: '+betterDate(subtask.createTime)+'<br/>結束: '+betterDate(subtask.endTime)+'</td>';
else
html[html.length] = '<td>開始: '+betterDate(subtask.createTime)+'</td>';
html[html.length] = '<td>'+subtask.name+'</td>';
html[html.length] = '<td>'+subtask.content.replace(new RegExp('\n','g'),'<br/>')+'</td>';
html[html.length] = '<td>'+subtask.progress+'</td>';
html[html.length] = '<td>'+subtask.time+'</td>';
html[html.length] = '<td>'+subtask.note+'</td>';
html[html.length] = '</tr>';
}
html[html.length] = '</tbody></table>';
html[html.length] = '<span style="margin-top:-15px"><i class="icon-tasks"></i> 已完成任務:'+ (data[id].length-count)+
' <i class="icon-tasks"></i> 當前任務數:'+count+'</span>';
html[html.length] = '</div>';
}
return html.join('');
}
隨著我構造這些Dom元素的Js程式碼越來越多,我的原型Html頁面的內容也就越來越少,少到最後很可能就只有一個header一個footer和一個空的div而已,其他所有內容,都是Ajax從伺服器讀取,拿到Json物件的返回之後,才開始構建Dom載入。
接下來,在使用AngularJS重構的時候,我發現頁面的原型設計好之後,基本上不需要做什麼改變,即所有的Dom元素,頁面上該看到的東西,都在你的Html中宣告出來了。僅僅看Html檔案,任何人都能知道這個頁面大概有哪些元素(表單、表格等各種UI控制元件),然後AngularJS用ng-model的方式讓你把這些Dom元素和資料進行了一種雙向繫結,即把你的資料(Data Model)宣告到頁面中。(我覺得這一步完全有可能由Web設計師來完成)下面的程式碼片段是一個例子:
<table class="table table-bordered table-hover" ng-controller="MachineCtrl">
<tr>
<th ng-click="machineKey = 'host'; reverse = !reverse" class="order-th">主機名</th>
<th ng-click="machineKey = 'address'; reverse = !reverse" class="order-th">IP地址</th>
<th>配置</th>
<th>賬號</th><th>OS</th>
<th>軟體</th>
<th>備註</th>
<th ng-click="machineKey = 'owner'; reverse = !reverse" class="order-th">使用者</th>
<th>操作</th>
</tr>
<tr ng-repeat="machine in machines | filter:query | orderBy:machineKey:reverse">
<td class="nowrap">{{machine.host}}</td>
<td class="nowrap">{{machine.address}}</td>
<td>{{machine.hardware}}</td>
<td>{{machine.name}} / {{machine.password}}</td>
<td>{{machine.os}}</td>
<td>{{machine.env}}</td>
<td>{{machine.note}}</td>
<td class="nowrap machine-owner" id="{{machine._id}}_owner">
<span>{{machine.owner}}</span>
<input type="text" class="form-control owner-input" ng-keydown="bookMachine($event, machine._id)"/>
<a class="owner-btn" ng-click="toggleOfflineBook(offline._id)"><i class="fa fa-times fg-lg"></i></a>
</td>
<td class="nowrap" ng-class="machine.status=='maintenance'?'disable':'enable'">
<span ng-click="maintainMachine(machine._id)"><i class="fa fa-cog fa-spin fa-lg"></i> 維護中...</span>
<a title="訂閱" ng-click="toggleMachineBook(machine._id)"><i class="fa fa-pencil"></i></a>
<a title="維護" ng-click="maintainMachine(machine._id)"><i class="fa fa-wrench"></i></a>
<a title="編輯" ng-click="editMachine(machine._id)"><i class="fa fa-edit"></i></a>
<a title="刪除" ng-click="deleteMachine(machine._id)"><i class="fa fa-trash-o"></i></a>
</td>
</tr>
</table>
可以看到我把一個table與叫machines的data model做了一個雙向繫結,而實際上machines就是一個Json陣列而已,當你用Js從服務端取到資料,填充了叫machines的Json陣列之後,這個table就會自動更新出來,每一行展示一個machine,每一個單元格展示machine上的某個屬性。所謂的雙向繫結,即一方產生變化的時候,另外一方就會同步更新。所以如果我在Js檔案中操作改變了這個machines陣列物件,例如刪除了其中一個元素那麼頁面table就會自動減少一行,更新了一個元素的屬性值,table也會自動更新。就感覺像頁面的宣告model是Js中Json物件的引用一樣,當Json物件發生任何變化,頁面的model引用也會隨之改變。
當具有這種特性之後,我的Js程式碼就可以專注對Model的管理和操作了,不需要再關心頁面的“資料”角色,頁面真正意義上充當了一個View的角色,我也不需要在Js中再去操作頁面上的資料和重構Dom元素之類的。(你可以選擇用jQuery.post,或者socket.io與伺服器通訊,在Js中維護Json物件的變化)當然,這種data-model binding並不是隨意的,而是分作用域的,某些model只在一定範圍的Html中可見。而控制這些作用域的就是ng-controller,即MVC中的C部分。
上面的Html中我將table宣告繫結了一個叫MachineCtrl的Controller,那麼machines這個model就只在這個table內可見。而Js中能操作它的也只有MachineCtrl這個Controller。我的Js關於MachineCtrl的程式碼片段如下:
/**
* Machine Controller
**/
machineApp.controller('MachineCtrl', function($scope, socket){
...
socket.on('machine list', function (data) {
$scope.machines = data;
});
$scope.editMachine = function(id){
var entity = $scope.findMachine(id);
var cloneEntity = cloneMachine(entity);
loadMachine(cloneEntity,'test');
}
$scope.bookMachine = function(e, id){
if(e.keyCode == 13){
var input = $('#'+id+'_owner input');
var owner = $.trim(input.val());
var entity = $scope.findMachine(id);
if(owner == entity.owner){
error('重複訂閱,忽略此次訂閱!');
$scope.toggleMachineBook(id);
return;
}else if(entity.owner.length !=0 && owner.length!=0){
error('機器已經被訂閱,請先取消訂閱');
return;
}
socket.emit('bookMachine',{'id': id, 'owner': owner});
e.preventDefault();
}else if(e.keyCode == 27){
$scope.toggleMachineBook(id);
e.preventDefault();
}
}...
當我用socket.io從服務端取到machines的Json物件之後,直接複製到MachineCtrl的$scope.machines上,這樣就實現了與頁面UI宣告瞭MachineCtrl裡的machines的雙向繫結。之後我與伺服器互動,就在MachineCtrl中更新$scope.machines實現增刪改查,頁面就會自動響應更新。總結:AngularJS確是一個很強大的Javascript前端MVC框架,如今的網際網路時代,對於富客戶端需求越來越旺盛,前端Js和Dom的互動越來越複雜,AngularJS很好的對它們進行了一種分層和維護管理。
相關文章
- Angularjs——初識AngularJSAngularJS
- 使用AngularJS學習MVC的基礎知識分享AngularJSMVC
- 初識 “HTML”HTML
- 初識GolangGolang
- 初識jQueryjQuery
- Nodejs初識NodeJS
- Express初識Express
- 初識GitGit
- 初識JSJS
- CDN初識
- 初識VueVue
- webpack初識Web
- 初識HIVEHive
- 初識TcpTCP
- 初識HTTPHTTP
- 初識ARKit
- 初識HaphoopOOP
- 初識PostgreSqlSQL
- AsterixDB初識AST
- 初識Fink
- 初識WebWeb
- 初識 Shell
- 初識 reduxRedux
- 初識 SpringMVCSpringMVC
- rocketmq初識MQ
- 初識 DockerDocker
- 初識:LevelDB
- 初識JVMJVM
- 初識JavaScriptJavaScript
- 初識MybatisMyBatis
- Kafka 初識Kafka
- 初識AJAX
- 初識promisePromise
- 初識JavaWEBJavaWeb
- Java初識Java
- 【springboot初識】Spring Boot
- 初識Kubernetes
- srpingboot 初識boot