AngularJS を使って Single Page Application を作成するなら、おそらく避けて通れないのがルーティング機能。例えば一覧と編集でビューを切り替えたい場合、ルーティング機能を使ってコントローラーとテンプレートを切り替えて描画する、はず。
先日作成したサンプルは一覧と入力が同じページに配置していたけど、AngularJS のルーティング機能を使ってこの2つを別ページに分けてみた。
まずは AngularJS を使ったコード。
<!DOCTPE html> <html ng-app="app"> <head> <meta charset="utf-8"> <title>Angular Sample</title> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js"></script> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular-resource.min.js"></script> <script type="text/javascript"> angular.module("app", ["ngResource"]). config(["$routeProvider", function($routeProvider) { $routeProvider. when("/", { // テンプレート文字列を指定 template: '<a href="#/new">New</a>' + '<ul>' + '<li ng-repeat="guest in guests">{{ guest.name }}</li>' + '</ul>', // テンプレートにバインドするコントロールの名前を指定 controller: "MainCtrl" }). when("/new", { // テンプレートの URL を指定 templateUrl: "/new.html", // テンプレートンバインドするコントローラーを指定 controller: NewCtrl }); }]); function MainCtrl($scope, $resource) { var Guest = $resource("/guests"); $scope.guests = Guest.query(); } function NewCtrl($scope, $resource, $location) { $scope.guestName = ""; // API の URL を指定してモデルを作成 var Guest = $resource("/guests"); $scope.addGuest = function() { Guest.save({ name: $scope.guestName }, function(guest) { // 作成に成功したら一覧に戻る $location.path("/"); }); $scope.guestName = ""; }; } </script> </head> <body> <!--ビューを埋め込む要素は ng-view でマークする--> <div ng-view></div> </body> </html>
ポイントは
- angular-resource.js を読み込む
- ルーティングの設定は $routeProvider を使って行う
- パスと、描画するテンプレートと、バインドするコントローラーを指定する
- テンプレートは文字列で渡してもいい
- テンプレートの URL を指定することもできる
- コントローラーは名前を文字列で指定することもできる
ぐらいかな。
Web API は Sinatra で実装しているけど、大して変わっていないので抜き出すのは省略し、今回作成したサンプルの全体を貼っておく。
# coding: utf-8 require "sinatra" require "json" # データはメモリ上に保存 GUESTS = [] get "/" do erb :home end # ゲスト一覧を JSON で取得する API get "/guests" do content_type :json, :charset => "utf-8" GUESTS.to_json end # ゲストを追加する API post "/guests" do # POST したデータを params から取得できなかったので # body を JSON にパース @guest = JSON.parse(request.body.read.to_s) GUESTS << @guest content_type :json, :charset => "utf-8" @guest.to_json end # 作成ページ用のテンプレートを返す get "/new.html" do erb :new end __END__ @@ home <!DOCTPE html> <html ng-app="app"> <head> <meta charset="utf-8"> <title>Angular Sample</title> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js"></script> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular-resource.min.js"></script> <script type="text/javascript"> angular.module("app", ["ngResource"]). config(["$routeProvider", function($routeProvider) { $routeProvider. when("/", { // テンプレート文字列を指定 template: '<a href="#/new">New</a>' + '<ul>' + '<li ng-repeat="guest in guests">{{ guest.name }}</li>' + '</ul>', // テンプレートにバインドするコントロールの名前を指定 controller: "MainCtrl" }). when("/new", { // テンプレートの URL を指定 templateUrl: "/new.html", // テンプレートンバインドするコントローラーを指定 controller: NewCtrl }); }]); function MainCtrl($scope, $resource) { var Guest = $resource("/guests"); $scope.guests = Guest.query(); } function NewCtrl($scope, $resource, $location) { $scope.guestName = ""; // API の URL を指定してモデルを作成 var Guest = $resource("/guests"); $scope.addGuest = function() { Guest.save({ name: $scope.guestName }, function(guest) { // 作成に成功したら一覧に戻る $location.path("/"); }); $scope.guestName = ""; }; } </script> </head> <body> <!--ビューを埋め込む要素は ng-view でマークする--> <div ng-view></div> </body> </html> @@ new <a href="#/">Back</a> <div> <input type="text" ng-model="guestName" placeholder="Input name"/> <button ng-click="addGuest()">Add</button> </div>
1ファイルで完結したかったので、AngularJS のルーティング機能で使うテンプレートすらもファイル内に書いて、Sinatra を経由で取得できるようにした。