AngularJS: TodoList
23 Jul 2015圖片來源:Static translations with AngularJS。
大約一年多前(還在前公司),RD 部門的工程師們 被迫 組了讀書會駭客任務,其中一個主題是研究當前火紅的 JavaScript 框架為之後平台改版做準備,當時還小不懂事的我看到 Ember.js 的可愛 Logo 便自告奮勇研讀。
最後公司選了 React …。居然就這樣了…都沒好好玩到 AngularJS XD
好吧,就當作是當年讀書會的複習!從 todo list 開始吧!
功能
- 搜尋:輸入關鍵字即可搜尋是否有符合的 todo 項目,但搜尋範圍僅限於標題。
- 新增:新增一個 todo 項目。
- 修改:修改特定 todo 項目。
- 刪除:刪除特定 todo 項目。
- 改變狀態 - 已完成 / 未完成:將 todo 的狀態標記為已完成或未完成。
- 計數:顯示全部、已完成、未完成的數目。
- 欄位驗證:提醒欄位必填。
說明
基本設定
將 todo list 存在陣列中,每個 todo 項目有欄位 id、title、status、edit。
- id:編號。
- title:標題。
- status:目前的狀態,0 為未完成,1 為已完成。
- edit:此 todo 項目是否在編輯的狀態,0 為否,1 為是。若處於編輯狀態,畫面會顯示編輯框、儲存鈕,且隱藏編輯鈕。
$scope.todos = [
{ id: 1, title: 'Buy milk', status: 1, edit: false },
{ id: 2, title: 'Call Mary', status: 0, edit: false },
{ id: 3, title: 'Visit Johe', status: 0, edit: false },
{ id: 4, title: 'Write an article', status: 0, edit: false },
];
並且,使用變數 counter 記錄下一次新增 todo 項目的 id。初始值為 todo 總數,每新增一次則加一。
$scope.counter = $scope.todos.length;
搜尋
在搜尋列中,我們利用ng-model="searchInput"
綁定<input>
,然後將每個會被重複的 todo item 區塊標註 filter 而能依照 searchInput 的值做篩選。
搜尋 input
利用ng-model="searchInput"
綁定<input>
。
<input type="text" placeholder="Search for..." ng-model="searchInput" />
todo item 區塊
filter: searchInput
表示依照 searchInput 的值做篩選。
<tr ng-repeat="x in todos | filter: searchInput">
...
</tr>
新增
我們可以在最後一行看到一個輸入框,輸入一些文字後按下「Add New Item」按鈕,即可新增新的項目。
<form class="todolistForm" name="todolistForm">
<td colspan="2">
<input
type="text"
class="form-control"
placeholder="type someting here..."
ng-model="newItem"
required
/>
</td>
<td>
<input type="submit" value="Add New Item" ng-click="addItem(newItem)" />
</td>
</form>
來看上面這段程式碼,ng-model="newItem"
綁定這個<inpu>
,然後利用ng-click
監聽「Add New Item」按鈕的 click 事件,當按下這個按鈕時,執行addItem()
這個 function,同時將 newItem 的值傳入 function。收到值,便更新 todos 陣列。
欄位驗證相關可參考 AngularJS Input Validation。
$scope.addItem = function(newItem) {
var newInputItem = angular.copy(newItem),
newItem = { id: $scope.counter + 1, title: newInputItem, status: 0 };
if (newItem.title != undefined) {
$scope.todos.push(newItem);
$scope.reset();
}
};
angular.copy
用於複製資料,我們取得資料(字串)後放到 newItem 這個物件的 title 欄位中。假設這個字串有值(即不為 undefined),那就加入到 todos 這個陣列裡面,然後 reset 輸入框(意即還原成尚未打字前的樣子)。
修改
我們分別使用ng-click="edit(x)
和ng-click="save(x)
綁在兩個按鈕「Edit」和「Save」上,當按下「Edit」時觸發edit()
,並出現輸入框來輸入要修改的字串;而當按下「Save」時觸發save()
,將修改好字串存回陣列中。
兩個按鈕「Edit」和「Save」。
<button type="button" class="btn btn-default" ng-click="edit(x)" ng-hide="x.edit">Edit</button>
<button type="button" class="btn btn-default" ng-click="save(x)" ng-show="x.edit">Save</button>
用來輸入要修改的字串的輸入框。只有在編輯狀態才會顯示,所以用ng-show="x.edit
這個指令設定顯示的時機。
<input
type="text"
id="edit-input-.{.{ x.id }.}."
class="form-control"
placeholder="type someting here..."
ng-show="x.edit"
/>
備註:由於部落格會把花括號吃掉,因此在左右加一個點,例如「.{.{ }.}.」。
編輯
將 edit 設為 true,開啟編輯狀態,並將目前的 title 值帶入輸入框中。
$scope.edit = function(item) {
var thisItem = item;
thisItem.edit = true;
document.getElementById('edit-input-' + thisItem.id).value = thisItem.title;
};
儲存
傳入此物件並取值,然後設定給 title。記得將 edit 設為 false,關閉編輯狀態。
$scope.save = function(item, obj) {
var thisItem = item,
thisInputValue = document.getElementById('edit-input-' + thisItem.id).value;
if (thisInputValue != '') {
thisItem.edit = false;
thisItem.title = thisInputValue;
}
};
刪除
使用ng-click="remove(x)
綁在按鈕「Remove」上,當按下「Remove」時觸發remove()
,移除整列項目。
<button type="button" class="btn btn-default" ng-click="remove(x)">Remove</button>
傳入此物件,並搜尋目前 todos 陣列中這個物件,找到就移除它。
$scope.remove = function(item) {
var thisItem = item,
index = $scope.todos.indexOf(item);
$scope.todos.splice(index, 1);
};
改變狀態:已完成 / 未完成
如果未完成,則出現以下程式碼,讓使用者可以按下「Done」將這個項目改為已完成的狀態。
<button type="button" class="btn btn-default" ng-click="done(x)" ng-hide="x.status">Done</button>
如果已完成,則出現以下程式碼,讓使用者可以按下「Undo」將這個項目改為未完成的狀態。
<button type="button" class="btn btn-default" ng-click="undo(x)" ng-show="x.status">Undo</button>
// 將這個項目改為已完成的狀態
$scope.done = function(item) {
var thisItem = item;
thisItem.status = 1; // 1:已完成
};
// 將這個項目改為未完成的狀態
$scope.undo = function(item) {
var thisItem = item;
thisItem.status = 0; // 0:未完成
};
計數
顯示全部、已完成、未完成的數目。
<div>
<span class="label label-default">All (.{.{ totalCount() }.}.)</span>
<span class="label label-success">Done (.{.{ inactiveCount() }.}.)</span>
<span class="label label-warning">Active (.{.{ activeCount() }.}.)</span>
</div>
備註:由於部落格會把花括號吃掉,因此在左右加一個點,例如「.{.{ }.}.」。
回傳符合條件的 todo 個數,純粹就是 arrray 的 length 計算。
$scope.totalCount = function() {
return $scope.todos.length;
};
$scope.activeCount = function() {
var activeArray = [];
angular.forEach(
$scope.todos,
function(value, key) {
if (value.status === 0) {
this.push(value);
}
},
activeArray,
);
return activeArray.length;
};
$scope.inactiveCount = function() {
var inactiveArray = [];
angular.forEach(
$scope.todos,
function(value, key) {
if (value.status === 1) {
this.push(value);
}
},
inactiveArray,
);
return inactiveArray.length;
};
欄位驗證
利用 HTML5 的屬性,在表單欄位上標註「required」表示必填,若空白則會出現對話框,提醒「請填寫這個欄位」。
Demo
來看一下完成品吧!
參考資料
這篇文章的原始位置在這裡-AngularJS - TodoList
由於部落格搬遷至此,因此在這裡放了一份,以便閱讀;部份文章片段也做了些許修改,以期提供更好的內容。