ここで作ったTODOアプリを改造する。今まで一つの画面だけで完結していたTODOアプリをあえてデータ受け渡しの伴う画面遷移があるアプリに作り変えてみる。
画面は二つ、TODO一覧画面とTODO入力画面だ。
まずはプロジェクト構成から。Chrome Packaged Appsなアプリのための構成になっている。
manifest.json
angular.min.js
background.js
app.js
index.html
list.html
detail.html
todo.css
calculator-128.png
calculator-64.png
今回重要なのは太字の箇所。それ以外は前回と同じ。
まずはindex.htmlから見てみよう。
<html ng-app="todo" ng-csp> <head> <script src="angular.min.js"></script> <script src="app.js"></script> <link rel="stylesheet" href="todo.css"> </head> <body> <h2>Todo</h2> <div ng-view></div> </body> </html>
ng-app="todo" でアプリ名を指定。
app.js にはコントローラーが記述されている。
画面遷移すると ng-view の部分が切り替わる。
次は一覧画面 list.html
<span>{{remaining()}} of {{todos.length}} remaining</span> [ <a href="" ng-click="archive()">archive</a> ] <br/> <input type="text" ng-model="search" class="search-query" placeholder="Search"> <table> <thead> <tr> <th>Todo</th> <th><a href="#/new">(New)</a></th> </tr> </thead> <tbody> <tr ng-repeat="todo in todos | filter:search | orderBy:'text'"> <td> <input type="checkbox" ng-model="todo.done"> <span class="done-{{todo.done}}">{{todo.text}}</span> </td> <td></td> </tr> </tbody> </table>
基本的には前回のTODOアプリと変わらないのだが、今回は、
<input type="text" ng-model="search" class="search-query" placeholder="Search">
と
<tr ng-repeat="todo in todos | filter:search | orderBy:'text'">
でフィルタとソートが実装されていたり、
入力画面へ飛ぶためのリンク
<th><a href="#/new">(New)</a></th>
があったりする。
次に入力画面 detail.html
<form name="myForm"> <div class="control-group" ng-class="{error: myForm.todoText.$invalid}"> <label>TODO</label> <input type="text" name="todoText" ng-model="todoText" placeholder="add new todo here" size="30" required> <span ng-show="myForm.todoText.$error.required" class="help-inline">Required</span> </div> <br> <a href="#/" class="btn btn-cancel">Cancel</a> <button ng-click="addTodo()" ng-disabled="myForm.$invalid" class="btn btn-primary">Add</button> </form>
<form name="myForm">
でフォーム名"myForm"を定義し、
<div class="control-group" ng-class="{error: myForm.todoText.$invalid}">
<input type="text" name="todoText" ng-model="todoText" placeholder="add new todo here" size="30" required>
<span ng-show="myForm.todoText.$error.required" class="help-inline">Required</span>
</div>
でテキストボックスに“入力必須”のバリデーション required を組み込んでいる。
まー特に説明の必要もないだろう。
では最後に最も重要なコントローラーの実装を見てみよう。
app.js
angular.module('todo', []). config(function($routeProvider) { $routeProvider. when('/', {controller:ListCtrl, templateUrl:'list.html'}). when('/new', {controller:CreateCtrl, templateUrl:'detail.html'}). otherwise({redirectTo:'/'}); }); function ListCtrl($scope, $rootScope) { $scope.remaining = function() { var count = 0; angular.forEach($rootScope.todos, function(todo) { count += todo.done ? 0 : 1; }); return count; }; $scope.archive = function() { var oldTodos = $rootScope.todos; $rootScope.todos = []; angular.forEach(oldTodos, function(todo) { if (!todo.done) $rootScope.todos.push(todo); }); }; } function CreateCtrl($scope, $rootScope, $location) { $rootScope.addTodo = function() { if(!$rootScope.todos){ $rootScope.todos = []; } $rootScope.todos.push({text:$scope.todoText, done:false}); $location.path('/'); }; }
1行目でアプリ名"todo"を指定し、2行目からrouteProviderを設定している。そこでは、URIとそれに紐づくコントローラーや画面テンプレートを定義している。
"/" が呼ばれたら "ListCtrl" が実行されて、 "list.html" が描画される、、、みたいな感じ。
そこから下はコントローラーの実装。今回はListCtrl(一覧画面用)とCreateCtrl(入力画面用)の二つだけ。
前回のTODOアプリと違うのは、TODO一覧の値 "todos" を $scope ではなく $rootScope で参照していること。こうすることで画面を跨いだ todos の受け渡しが可能となる。
CreateCtrlの最後の $location.path('/'); という行は入力画面コントローラー処理から抜けるために飛び先 "/" を指定している。
さらりと説明したが、今回最も大切なのは app.js の中の routeProvider の設定と $rootScope である。自分は$rootScopeの存在を知らずに少しハマってました。
参考
NEXTCODE BLOG: AngularJSでController間の通信を行う
Angular.jsについてちょっとしゃべる - slideshare
関連エントリ
JS用MVCフレームワーク「AngularJS」を使ってChrome Packaged Apps【基本編】
【Chrome】Packaged Apps アプリをHelloWorld【Google】
スマホOSの主導権争いの行方