為了降低前端代碼的數量,提高可維護性,可測試性,學習了下AngularJS,正在准備投入項目開發中。
AngularJS的概念比較多,如果面向對象方面的書理解的不透的話學習起來有些費勁,它的官方有個快速入門不錯,中文版如下
http://www.ituring.com.cn/minibook/303
但除了入門外,要真實的寫項目還是得把模塊划分,依賴關系處理,組件間通信,文件目錄安排等問題解決好才能干活。
根據這個學習目的,寫了個DEMO,地址如下
http://onlytiancai.github.io/codesnip/angular-demo1.html
- 頁面初始化時有3個蘋果,3個桔子,用戶可以在輸入框里重新輸入桔子和蘋果的數量,界面會有相應的變化
- 定義了兩個模塊
- common是通用模塊,
- 包含一個commonErrorMsg的directive用來顯示全局的錯誤信息, 通過監聽common.showerror事件來獲取信息,並讓字體顯示為紅色
- myApp是整個單頁面應用的模塊,
- 包含inputCtrl, statusCtrl兩個controller
- 包含fruits, orange, apple三個directive
- 包含range的filter
- 包含fruitsService的service
- common是通用模塊,
- 總體依賴關系如下
- myApp依賴common
- fruits, inputCtrl, statusCtrl都依賴fruitsService
- inputCtrl通過事件隱含依賴common
- 總體來說上層module依賴底層module,上層controller依賴底層service
- fruits是一個自定義的directive,用來顯示所有水果
- transclude=True表示它的子元素也受它管理,比如里面的時蘋果和桔子
- 該directive要和inputCtrl進行通信,以便動態更改水果的數量, 所以它和inputCtrl共同依賴fruitsService,並通過fruitsService的事件進行通信。
- 事件基本是全局的,所以定義事件時盡量有個命名空間, 如common.showerror, fruitsService.updated
- orange和apple是兩個很普通的directive,其中apple還掩飾了directive里如何處理自身的UI事件
- statusCtrl就監聽fruitsService.updated事件,並更新自己的狀態
- inputCtrl里watch自身UI里的兩個ng-model,適時調用fruitsService的相關方法
- 如果界面輸入太大的數字,會向common.showerror發送消息,以在界面上提示給用戶 這里沒有用ng-form自帶的驗證就是為了演示模塊間如何通信
- range的filter是彌補ng-repeat的不足,讓它支持類似 x in range(10)的形式
- fruitsService純粹是為了directive之間或controller之間通信和共享數據所設計
HTML代碼
<!doctype html> <html ng-app="myApp"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script src="angular.js"></script> <script src="angular-demo1.js" charset="utf-8"></script> <title>AngularJS Demo</title> </head> <body> <p common:error_msg></p> <p ng-controller="statusCtrl">一共有{{apple_count}}個蘋果,{{orange_count}}個桔子。</p> <fruits> <apple ng-repeat="n in [] | range:apple_count"></apple> <orange ng-repeat="n in [] | range:orange_count"></orange> </fruits> <p ng-controller="inputCtrl">請輸入 <input type="text" ng-model="apple_count" />個蘋果, <input type="text" ng-model="orange_count" />個桔子 </p> </body> </html>
js代碼
angular.module('common', []);
angular.module('common').directive('commonErrorMsg', function(){
return {
restrict: "A",
controller: function ($scope, $element, $attrs) {
$element.css('color', 'red');
$scope.$on('common.showerror', function (ev, msg) {
$element.html(msg);
});
}
}
});
var myApp = angular.module('myApp', ['common']);
myApp.directive('fruits', function(fruitsService) {
return {
restrict: "E",
transclude: true,
replace: true,
template: '<ul ng-transclude></ul>',
controller: function ($scope, $element, $attrs) {
$scope.$on('fruitsService.updated', function () {
$scope.apple_count = fruitsService.apple_count;
$scope.orange_count = fruitsService.orange_count;
});
}
}
})
.directive('orange', function() {
return {
restrict: "E",
template: '<li>桔子</li>'
}
})
.directive('apple', function() {
return {
restrict: "E",
template: '<li><a ng-click="show()" href="#">蘋果</a></li>',
link: function(scope, element, attrs) {
scope.show = function(){
alert('我是一個蘋果');
};
}
}
})
.controller('statusCtrl', function($scope, fruitsService) {
$scope.$on('fruitsService.updated', function () {
$scope.apple_count = fruitsService.apple_count;
$scope.orange_count = fruitsService.orange_count;
});
})
.controller('inputCtrl', function($scope, fruitsService, $rootScope) {
$scope.$watch('apple_count', function (newVal, oldVal, $scope) {
if (newVal > 10){
$rootScope.$emit('common.showerror', '蘋果數量太多了');
}else{
fruitsService.set_apple_count(newVal);
}
}, true);
$scope.$watch('orange_count', function (newVal, oldVal, $scope) {
if (newVal > 10){
$rootScope.$emit('common.showerror', '桔子數量太多了');
}else{
fruitsService.set_orange_count(newVal);
}
}, true);
fruitsService.set_apple_count(3);
fruitsService.set_orange_count(2);
})
.filter('range', function() {
return function(input, total) {
total = parseInt(total);
for (var i=0; i<total; i++)
input.push(i);
return input;
};
})
.service('fruitsService', function ($rootScope) {
this.set_apple_count = function (apple_count) {
this.apple_count = apple_count;
$rootScope.$broadcast('fruitsService.updated');
};
this.set_orange_count = function (orange_count) {
this.orange_count = orange_count;
$rootScope.$broadcast('fruitsService.updated');
};
});
下面這篇帖子也很好,關於如何用AngularJS開發大型項目的。
