AngularJSに挑戦！入門 ToDoをつくろう

#はじめに

AngularJSを使ったアプリケーションの作成に際して、
ToDoがもっとも簡単かつ覚えるのに適しているように思います。
（他のサイトでもよく見かけますし）
ですので、ToDoアプリを作ります。

#要件

ToDoアプリを作るにあたって、何を作ればいいか最初に考えます。
1. Todoの追加
2. ToDo一覧の表示
3. 各ToDoに完了チェックボタンを付ける
4. 各ToDoを変更する
5. 各ToDoを削除する
6. 未完了数/完了数の表示

以下は、あったらいいな機能
1. フィルター
2. すべて完了にする
3. 追加・更新日時を表示

ほかにもありますが、今回はこの辺までとします。

#機能実装
##メイン

```lang:Todo.js
var TodoApp = angular.module('TodoApp', []);

TodoApp.controller('TodoCtrl', ['$scope', function ($scope) {
    $scope.todos = [];
    index = 0;

    $scope.add = function() {
        $scope.todos.push({id: index, message: $scope.message, done: false});
        $scope.message = "";
        index++;
    };

}]);
```

1. 配列`$scope.todos`を初期化します。
2. `index`は、一意な数字を設定します。
3. `$scope.todos.push()`で、配列に追加します。

```lang:Todo.html
<html>
<head>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.1/angular.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
    <link rel="stylesheet" href="Todo.css">
    <script type="text/javascript" src="Todo.js"></script>
    <title>Todo</title>
</head>
<body ng-app="TodoApp">
    <div class="container">
        <div class="jumbotron">
            <h2>Todo</h2>
        </div>
        <div ng-controller="TodoCtrl">
            <div class="bg-info">
                <label>ToDo: <label><input type="text" ng-model="message"><button ng-click="add()">追加</button>
            </div>
            <table class="table table-striped">
                <tr>
                    <th></th>
                    <th>ToDo:></th>
                </tr>
                <tr ng-repeat="todo in todos">
                    <td><input type="checkbox" ng-model="todo.done"></td>
                    <td><span class="done-{{todo.done}}">{{todo.message}}</span></td>
                </tr>
            </table>
        </div>
    </div>
</body>
</html>
```

```lang:Todo.css
.done-true {
    text-decoration: line-through;
    color: grey;
}
```

1. `<button ng-click="add()">`は、クリックしたときに、`add()`関数を呼び出します。
2. `<input type="checkbox" ng-model="todo.done">`完了・未完了のチェックボックスを表示しています。
3. `<span class="done-{{todo.done}}">{{todo.message}}</span>`でメッセージの表示・非表示をしています。class="done-{{todo.done}}"で、CSSの設定を変更しています。

これで、追加はできました。


##変更・削除を作る

変更・削除機能を追加します。
$scope.addの下に追加します。

```lang:Todo.js
    $scope.update = function(todo) {
        var message = window.prompt("変更",todo.message);
        if (message) {
            $scope.todos.forEach(function(td) {
                if (td.id == todo.id) {
                    td.message = message;
                }
            });
        }
    }

    $scope.delete = function(todo) {
        var index = 1;
        var t;
        for (var i = 0; $scope.todos.length; i++) {
            t = $scope.todos[i];
            if (t.id == todo.id) {
                index = i;
                break;
            }
        }
        $scope.todos.splice(index, 1);
    }
```

1. 更新は、`forEach`で配列を回して、一致する`id`を探し、`message`を更新します。
2. 削除は、`for`で配列を回して、一致する`id`を探し、`splice()`で配列から削除します。


```lang:Todo.html
<html>
<head>
    <meta charset="utf-8">
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.1/angular.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
    <link rel="stylesheet" href="Todo.css">
    <script type="text/javascript" src="Todo.js"></script>
    <title>Todo</title>
</head>
<body ng-app="TodoApp">
    <div class="container">
        <div class="jumbotron">
            <h2>Todo</h2>
        </div>
        <div ng-controller="TodoCtrl">
            <div class="bg-info">
                <label>ToDo: <label><input type="text" ng-model="message"><button ng-click="add()">追加</button>
            </div>
            <table class="table table-striped">
                <tr>
                    <th></th>
                    <th>ToDo</th>
                    <th>削除</th>
                </tr>
                <tr ng-repeat="todo in todos">
                    <td><input type="checkbox" ng-model="todo.done"></td>
                    <td><span class="done-{{todo.done}}" ng-click="update(todo);">{{todo.message}}</span></td>
                    <td><button ng-click="delete(todo);">削除</button></td>
                </tr>
            </table>
        </div>
    </div>
</body>
</html>
```

1. 先ほどのソースから、tableタグ内を修正します。
2. テキストをクリックしたら、変更できるように`ng-click="update(todo);`イベントを追加します。
3. `<button ng-click="delete(todo);">`削除ボタンを追加します。

##未完了数/完了数の表示

先ほどのソースの`$scope.delete`の後ろに以下のコードを追加します。

```lang:Todo.js
    $scope.remaining = function() {
        var count = 0;
        $scope.todos.forEach(function(todo) {
            count += todo.done;
        });
        return count;
    }
```

単に配列を`forEach`で回して、完了していたら`count`を１つ上げています。

```lang:Todo.html
            <table class="table table-striped">
                <tr>
                    <th>{{remaining()}}/{{todos.length}}</th>
                    <th>ToDo</th>
                    <th>削除</th>
                </tr>
```

先ほどのソースのthタグを変更します。

##使ってみよう

以下のようになりましたか？

![todo01.jpg](https://qiita-image-store.s3.amazonaws.com/0/57930/ee5c88a9-a732-f355-556c-772d4199a95a.jpeg)

1. ToDo: 欄から入力して、一覧に追加されます。
2. 一覧でチェックを付けると、取り消し線が引かれます。
3. 一覧のメッセージをクリックすると、入力ダイアログが表示されて変更できます。
4. 削除ボタンを押せば、削除できます。

#機能拡張
あったらいいな機能を追加してみましょう。

##フィルター

```lang:Todo.html
            <table class="table table-striped">
                <tr>
                    <th>{{remaining()}}/{{todos.length}}</th>
                    <th>ToDo: フィルター<input type="text" ng-model="q"></th>
                    <th>削除</th>
                </tr>
                <tr ng-repeat="todo in todos | filter:q">
                    <td><input type="checkbox" ng-model="todo.done"></td>
                    <td><span class="done-{{todo.done}}" ng-click="update(todo);">{{todo.message}}</span></td>
                    <td><button ng-click="delete(todo);">削除</button></td>
                </tr>
            </table>
```

1. thタグに`フィルター<input type="text" ng-model="q">`を追加します。`q`にフィルター文字列が入ります。
2. `<tr ng-repeat="todo in todos">`に` | filter:q`を追加します。AngularJSのフィルター機能です。

##すべて完了にする

`$scope.remaining`の後ろに追加します。

```lang:Todo.js
    $scope.complate = function() {
        $scope.todos.forEach(function(todo) {
            todo.done = true;
        });
    }
```

```lang:Todo.html
<th>{{remaining()}}/{{todos.length}}<button ng-click="complate();">すべて完了</button></th>
```

ヘッダー部分にボタンを追加します。


##追加・更新日時を表示

`$scope.add`と`$scope.update`を変更して、`$scope.complate`の後ろに
`$scope.getDate`を追加します。

```lang:Todo.js
    $scope.add = function() {
        $scope.todos.push({id: index, message: $scope.message, date: $scope.getDate(), done: false});
        $scope.message = "";
        index++;
    };

    $scope.update = function(todo) {
        var message = window.prompt("変更",todo.message);
        if (message) {
            $scope.todos.forEach(function(td) {
                if (td.id == todo.id) {
                    td.message = message;
                    td.date = $scope.getDate();
                }
            });
        }
    }

    :
    :

    $scope.getDate = function() {
        var date = new Date();

        return date.getFullYear() + "/" +
               date.getMonth() + "/" +
               date.getDate() + " " +
               date.getHours() + ":" +
               date.getMinutes() + ":" +
               date.getSeconds()
    }
```

tableタグ内を変更します。

```lang:Todo.html
            <table class="table table-striped">
                <tr>
                    <th>{{remaining()}}/{{todos.length}}<button ng-click="complate();">すべて完了</button></th>
                    <th>ToDo: フィルター<input type="text" ng-model="q"></th>
                    <th>更新日時</th>
                    <th>削除</th>
                </tr>
                <tr ng-repeat="todo in todos | filter:q">
                    <td><input type="checkbox" ng-model="todo.done"></td>
                    <td><span class="done-{{todo.done}}" ng-click="update(todo);">{{todo.message}}</span></td>
                    <td><span>{{todo.date}}</td>
                    <td><button ng-click="delete(todo);">削除</button></td>
                </tr>
            </table>
```

##使ってみよう

以下のようになりましたか？

![todo02.jpg](https://qiita-image-store.s3.amazonaws.com/0/57930/cbdfa653-11f5-feea-6bb7-c5fce8889530.jpeg)

1. フィルター機能で、絞込みができるようになった
2. すべて完了ですべてのToDoが完了状態になる
3. 更新日時が表示される

#おわりに

以上でAngularJSのToDoが作成できました。

今回作成したソースコードは、[https://github.com/village333/angularJS_ToDo](https://github.com/village333/angularJS_ToDo)に
登録してありますので、参考にしてください。