AngularJS - TodoList

AngularJS - TodoList

AngularJS

圖片來源:Static translations with AngularJS。

大約一年多前(還在前公司),RD部門的工程師們 被迫 組了讀書會駭客任務,其中一個主題是研究當前火紅的JavaScript框架為之後平台改版做準備,當時還小不懂事的我看到 EmberJS 的可愛LOGO便自告奮勇研讀。  

EmberJS

最後公司選了 React ...。居然就這樣了...都沒好好玩到AngularJS XD

好吧,就當作是當年讀書會的複習!從TodoList開始吧!

功能

  • 搜尋:輸入關鍵字即可搜尋是否有符合的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」按鈕,即可新增新的項目。

HTML

<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

JS

$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(),將修改好字串存回陣列中。

HTML

兩個按鈕「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">

JS

編輯

將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(),移除整列項目。

HTML

<button type="button" class="btn btn-default" ng-click="remove(x)">Remove</button>

JS

傳入此物件,並搜尋目前todos陣列中這個物件,找到就移除它。

$scope.remove = function(item){
    var thisItem = item,
        index = $scope.todos.indexOf(item);
    $scope.todos.splice(index, 1);       
};

改變狀態:已完成 / 未完成

HTML

如果未完成,則出現以下程式碼,讓使用者可以按下「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>

JS

//將這個項目改為已完成的狀態
$scope.done = function(item){
    var thisItem = item;
    thisItem.status = 1; //1:已完成
};

//將這個項目改為未完成的狀態
$scope.undo = function(item){
    var thisItem = item;
    thisItem.status = 0; //0:未完成
};

計數

顯示全部、已完成、未完成的數目。

HTML

顯示全部、已完成、未完成的數目。

<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>

JS

回傳符合條件的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;
};

欄位驗證

在表單欄位上標註「required」表示必填,若空白則會出現對話框,提醒「請填寫這個欄位」。

欄位驗證

Demo

來看一下完成品吧!

AngularJS - TodoList

Source Code


參考資料


由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-AngularJS: TodoList
張貼留言