最近一段時間一直在看AngularJS,趁著一點時間總結一下。
先推薦幾個教學
1. AngularJS入門教學 比較基礎,是官方Tutorial的翻譯。
2. 七步驟從AngularJS菜鳥到專家 也比較基礎,製作了一個線上音樂播放網站。
3. AngularJS開髮指南 這個教學比較全面,但我覺得翻譯的有些晦澀難懂。
看過這些教學後,覺得AngularJS也懂一點了,就想用它做點事,就分析一下AngularJS寫的todomvc吧。
Todomvc官網網址:http://todomvc.com/
專案的目錄如下:
bower_components裡放了兩個資料夾,其中angular資料夾是用來一如angular.js檔案的,todomvc-common資料夾裡的放入了所有todo專案統一的cssjs(只是用來產生左側內容的,與項目無關)和圖片。
js資料夾是大頭,裡面放了對應的controller(控制器)directive(指令)service(服務)和app.js。
test資料夾裡放的是測試用的程式碼,不分析。
index.html是專案的view頁。
先來看看app.js
就是定義了一個模組todomvc
再看一下services下的todoStorage.js
使用factory方法建立了todoStorage的service方法,而這個service方法的本質就是回傳了兩個方法get和put,兩者都是用了JSON2和HTML5的特性。 get將todos的內容從localStorage中取出,並解析成JSON,put將todos轉換成JSON字串,並儲存到localStorage中。
再看一下directives下面的兩個指令檔。
todoFocus.js
傳回function的參數中,elem就是包含該指令的元素的數組,attrs是元素的所有屬性、屬性名稱等組成的物件。
其中用到了兩個AngularJS的方法
$watch(watchExpression, listener, objectEquality) 註冊一個偵聽器回調,每當watchExpression變更時,監聽回呼將會被執行。
$timeout(fn[, delay][, invokeApply]) 當timeout的值達到時,執行fn函數。
todoFocus.js建立了todoFocus指令。當一個元素擁有todoFocus屬性時,該指令會為該元素的todoFocus屬性的值添加監聽,如果todoFocus屬性的值改變成true,就會執行$timeout(function () {elem[0].focus(); }, 0, false);其中的延遲時間為0秒,所以會立即執行elem[0].focus()。
todoEscape.js
todoEscape.js創建了todoEscape指令。當按下Escape鍵時,執行attrs.todoEscape的表達式。
看一下大頭,controllers資料夾中的todoCtrl.js,這個檔案略長,我就直接寫註解了。
// 記錄新的todo
$scope.newTodo = '';
// 記錄編輯過的todo
$scope.editedTodo = null;
// 當todos的值改變時執行其中的方法
$scope.$watch('todos', function (newValue, oldValue) {
// 取得未完成的todos的數目
$scope.remainingCount = filterFilter(todos, { completed: false }).length;
// 取得已完成的todos的數目
$scope.completedCount = todos.length - $scope.remainingCount;
// 當且僅當$scope.remainingCount為0時,$scope.allChecked為true
$scope.allChecked = !$scope.remainingCount;
// 當todos的新值和舊值不相等時,存入todos
if (newValue !== oldValue) { // This prevents unneeded calls to the local storage
todoStorage.put(todos);
}
}, true);
if ($location.path() === '') {
// 若$location.path()為空,則設為/
$location.path('/');
}
$scope.location = $location;
// 當location.path()的值改變時執行其中的方法
$scope.$watch('location.path()', function (path) {
// 取得狀態的過濾器
// 若path為'/active',濾波器為{ completed: false }
// 若path為'/completed',則過濾器為{ completed: true }
// 否則,過濾器為null
$scope.statusFilter = (path === '/active') ?
{ completed: false } : (path === '/completed') ?
{ completed: true } : null;
});
// 新增一個新的todo
$scope.addTodo = function () {
var newTodo = $scope.newTodo.trim();
if (!newTodo.length) {
return;
}
// 在todos裡增加一個todo,completed屬性預設為false
todos.push({
title: newTodo,
completed: false
});
// 置空
$scope.newTodo = '';
};
// 編輯一個todo
$scope.editTodo = function (todo) {
$scope.editedTodo = todo;
// Clone the original todo to restore it on demand.
// 儲存編輯前的todo,並為恢復編輯前做準備
$scope.originalTodo = angular.extend({}, todo);
};
// 編輯todo完成
$scope.doneEditing = function (todo) {
// 置空
$scope.editedTodo = null;
todo.title = todo.title.trim();
if (!todo.title) {
// 若todo的title為空,則移除該todo
$scope.removeTodo(todo);
}
};
// 恢復編輯前的todo
$scope.revertEditing = function (todo) {
todos[todos.indexOf(todo)] = $scope.originalTodo;
$scope.doneEditing($scope.originalTodo);
};
// 移除todo
$scope.removeTodo = function (todo) {
todos.splice(todos.indexOf(todo), 1);
};
// 清除已完成的todos
$scope.clearCompletedTodos = function () {
$scope.todos = todos = todos.filter(function (val) {
return !val.completed;
});
};
// 標記所有的todo的狀態(true或false)
$scope.markAll = function (completed) {
todos.forEach(function (todo) {
todo.completed = completed;
});
};
});
最後看一下index.html,這個文件我們一段一段的分析。