[AugularJS]todomvcのソースコードで基本的な使い方を確認・復習(夏休み企画3)

前回に引き続き、AnguarJSに関しての自習

前回・前々回で、基本的なmoduleAPIとdirectiveに関してある程度調べたので、
次は実際にプロジェクトで記述されているレベルのものを、読みといていこうと思います。

本当はserviceに関して($http, $rocate, $routeProviderなど)に関して書こうと
思ってたんですが、ググってたらとてもいいサンプルがあったので、読んでみました。

公式で紹介されている、シンプルなtodoアプリを自分で機能追加したものも書いてみたんですが、
それは機会があれば。

tastejs/todomvc -github-

様々なフレームワークを利用してTODOアプリを書いてみたものが、豊富に公開されています。
backborn.jsとかknockout.js、jqueryまでありますので、比較すると面白いかもしれません。

実際に動いている状態のものはこちら

今回は無論AngularJSのサンプルを見ていきます。

まずはindex.htmlで呼び出されているjsファイルを確認

こんな感じ。

公式で推奨されているように、それぞれファイルを分割していますね。

app.jsでmouduleをグローバルで定義して、各ファイルでそのmoduleを拡張しています。

外部moduleなどを使っていない点、チュートリアルとして、とても良いと思います。

AngularJS関連のファイルの構成を確認

●app.js

todomvcというmoduleを定義するだけのファイル。

●jsディレクトリ内のサブディレクトリ

複数ファイルがあるうちの1つを取り上げますが、内容はだいたい同じ。

‘todoFocus’というattributreを定義しています。todoFocusというプロパティの変化に応じて、
処理を行うdirectiveのようです。 詳細は後ほど。今はザックリと構成を追っていきます。

todomvcというapp.jsで定義されたmoduleを他のファイルで拡張している点に注目です。

この辺は使われているプロジェクトによって、結構違いがあるんですが、こういう拡張もいいですね。
こういう形式にしたほうが、directive専用のmoduleを定義して、
app.jsでインポートするより、変更は少ないし、拡張はしやすいかもしれません。

その代わり、他のプロジェクトに、このmoduleの一部を持ち出したいときは、ソースの変更が必要になりますが。

index.htmlを確認して、処理を追いかける。

bodyタグすぐ下のsectionタグでコントローラーを指定

このページのほとんど全体を含んでいるタグです。
このページでは’TodoCtrl’というコントローラー1つで全ての処理を行うようです。

id=”header”の箇所

簡単ですね。TodoCtrlの$scope.newTodoに、input#new-todoが対応しています。formのsubmitが行われると
TodoCtrlの$scope.addTodo()が実行される、という流れ。

それでは$scope.addTodo()の中身を確認。

これも簡単ですね。$scope.newTodo(つまり、input#new-todoのvalue)から
空白文字と終端文字を削除(trim()関数)して、newTodo変数に格納。

それから、newTodoの入力チェックして(!newTodo.length)ちゃんと入力があれば
todo一覧にnewTodoの中身をpush(todos.push)して、あとは$scope.newTodoの中身をリセット($scope.newTodo = ”;)

いたってシンプルです。

section#mainの箇所

ng-show=”todos.length”は、todosの要素数が1以上なら、このElement内を表示、って感じです。

ここから順番に見ていきます。

またinputタグ。チェックボックスですね。$scope.allCheckedに対応。クリックすることで、$scope.markAll(allChecked)が
実行されます。

大体予想がつきますが、一応、$scope.markAll()の処理を確認してみます。

$scope.totodsをforEachで回して、それぞれの要素をtodoとして、その中のcompletedを
チェックボックス(引数にあるcompletedで、
つまりinput#toggle-all)にチェックがあれば、true、なければfalseを設定しています。

これによって、全ての要素に対して一度にチェックしたり、外したりしていますね。

angular.forEach(objects, function(){…})の形式は多用されるので覚えておくと便利です。

※個人的には、引数の名前がcompletedというのはミスなのかな・・・と思ってます。
生意気ですが、isAllCheckedとかのほうがいいんじゃないかな・・・。と個人的には思います・・・。

更に進んでいきます。

このアプリ最大のキモである、todo一覧を表示する箇所です。(view)

上から順番に

$scope.todosの要素数だけ繰り返します。

ポイントは、filter:statusFilterの部分。

ここで、何をしているか、というとlocation.path()を監視して、変化があれば$scope.statusFilterを常に切り替えてます。
具体的に言うと

  • URL が****/activeならcompleted:false
  • URL が****/completedならcompleted:true
  • それ以外は全てnull

を設定しています。これによって、URLによって

  • 終了していないtodoの一覧を表示する
  • 終了したtodoの一覧を表示する
  • 全て表示する

を切り替えています。かしこいー。

URLの切り替えは

この部分のaタグで切り替えてますね。

つぎは、いよいよtodoそのものが表示されている部分

上から順番に。

最初はチェックボックス、モデルは$scope.todo.completedです。このチェックボックスを切り替えることに
よって、対象のtodoが完了か、そうでないかを判断・切替ができるようになってます。

つぎはtodoの内容({{todo.text}})です。ダブルクリックすると(ng-dblclick)すると$scope.editTodo(todo)
が動作するようになっていますね。

$scope.editTodo()を見てみます。

ダブルクリックされた$scope.todoを引数に受け取り、$scope.editedTodoにコピーしてますね。
これ、ちょっと、いまは意味不明ですが、angular.extend({}, todo)で、これまた$scope.originalTodoに
todoオブジェクトをコピーしています。これの違いは後ほど。

最後はボタン。クリックするとremoveTodo(todo)が実行されます。

処理内容は以下

これはそのまんま。todosのなかからtodoと同じものを探して、そこから1つを削除。
つまり、todosのなかのtodoのみを削除。それだけです。

そして最後。

ここに今回、説明がめんどくさい部分が全て集結していると言っても
過言ではない。

まずは見慣れない、attirbuteが3つ。todo-blurとtodo-escapeとtodo-focus。
それぞれに何かしらのイベントかなんかが、設定されています。

これは最初のほうに説明した、独自directiveです。

これらが何をしているか、それぞれのファイルを見てみます。

まずはtodo-escape.js

ごめんなさい、初見だと意味わかんない。

要するに、keydownされたときのeventを監視してるんですが、押されたkeyがESCだったときに
scope.$apply(attrs.todoEscape);を実行する、感じですね。

じゃぁ$applyとは何か。ものすごくざっくり言うと、引数に受け取った関数を実行する、的な感じ。

面白いな、と思ったのが、todo-escapeもtodo-blurもtodoBlurとして定義されているんですね。
それで、ESCが押されたか、そうでないかで実行する処理を変えてます。

上記のtodo-escape.jsでは’todoBlur’というattirbuteがついたDOMエレメントが、keydownでESCが押された場合は
そのDOMエレメントのtodoEscapeというattributeの内容が関数として実行されます。

つまり、上記の場合だとrevertEditing(todo)という処理が実行される。うまくできてるなー・・・。

ここでの処理は、todos内のtodoと同じオブジェクトに、編集前のオブジェクトを挿入しています。
その後は$scope.doneEditing($scope.originalTodo)を実行。これは後ほど。

うまく説明できてないと思いますが、ソースコード読んでもらえれば納得できると思います。
todo-escape.jsというファイルでありながら、todoEscapeというdirectiveが定義されているわけではないんですね。
あくまで、todoEscapeという属性に書かれた関数を取るため。イベントの監視自体はtodoBlurという属性で監視しています。

次はtodo-blur.js

これはシンプルですね。対象のDOMエレメントに対して、blurのイベントをbindして、
todoBlurに設定された関数を実行する、という感じです。

この形式は、非常に汎用性が高いので、覚えておくと便利かもしれません。

todoBlurに設定されているイベントはdoneEditing(todo)ですね。さきほどの$scope.revertEditing()
内でも呼び出されていました。

こちらは$scope.editedTodo(編集中のtodo)をnullにして、todo.titleから空白文字、終端文字を削除。
最後に、todo.titleが空っぽなら、$scope.removeTodo(todo);を実行。

最後にtodo-focus.js

これも、よく見かける形式です。todoFocusに設定された値(”todo == editedTodo”)を監視して、
この値がtrueなら

を実行します。

これで、先ほど後回しにした

の話が出てきます。
要素をダブルクリックすると$scope.editTodoに、クリックしたtodoが設定されます。(変更前のものも、$scope.originalTodoにコピーしておきます)

これが変更されると、todoFocusというattributeによって、”todo == editedTodo”が評価されます。

editedTodoはtodoがコピーされているので、対象のDOMエレメントがfocus()されます。

これによって、ユーザがテキストボックスに入力できるようになります。

入力が終了して、blurされるか、ESCキーがkeydownされると、前述した、それぞれの処理へ。

こんな感じです。

これで全部。

今回説明しなかったところをざっくりと。

  1. データは全てlocal strageに保存されている。
  2. データの新規作成、詳細の取得、編集、削除はfactoryのgetとputで管理している
  3. TodoCtrl内でtodos(todoの一覧)のデータを$watch(変更の監視)をすることによって、各種処理で変更があった場合は
    factoryを通してデータを処理している

これでソースコードの全部を説明終わり。
上記3点によって、完全にHTML/CSS/javascriptのアプリケーションになってますね。
local strageを利用しているのでオフラインでも利用できます。

絶対わかりづらいと思うけど、ごめんなさい。

このソースコードが実際に動作しているのは

todos

にて、確認できます。ソースコードと並べて動作を見ると本当に勉強になります。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です