ビューを入れ子にする
さらに、UI Routerではビューを入れ子にすることもできます。たとえば、/membersではメンバーの一覧情報を表示し、一覧から特定のメンバーを表示すると、それぞれ/members/1、/members/7のようなパスで個別メンバーの情報が表示される、といったケースです。
![入れ子になったビューを定義](http://cz-cdn.shoeisha.jp/static/images/article/9012/9012_003_s.gif)
このようなルートは、以下のように定義できます。
<!DOCTYPE html> <html ng-app="myApp"> <head> <meta charset="UTF-8" /> <title>AngularJS</title> <script src="bower_components/angular/angular.min.js"></script> <script src="bower_components/angular-ui-router/release/angular-ui-router.min.js"></script> <script src="scripts/nest.js"></script> </head> <body> <div ui-view></div> </body> </html>
angular.module('myApp', [ 'ui.router' ]) .config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) { $stateProvider // membersステートを準備 .state('members', { url: '/members', controller: ['$scope', 'GetMembers', function($scope, GetMembers) { $scope.members = GetMembers(); }], template: '<ul>' + '<li ng-repeat="(id, member) in members">' + '<a ui-sref=".details({ id: id })">' + '{{member.name}}</a></li>' + '</ul><hr />' + '<div ui-view></div>' }) // membersステートの子ステートmembers.detailsを準備 .state('members.details', { url: '/:id', controller: ['$scope', '$stateParams', 'GetMembers', function($scope, $stateParams, GetMembers) { $scope.member = GetMembers()[$stateParams.id]; }], template: '<table class="table">' + '<tr>' + '<th>名前</th>' + '<td>{{member.name}}</td>' + '</tr>' + '<tr>' + '<th>性別</th>' + '<td>{{member.sex}}</td>' + '</tr>' + '<tr>' + '<th>誕生日</th>' + '<td>{{member.birth | date : "yyyy年MM月dd日"}}</td>' + '</tr>' + '</table>' }); $urlRouterProvider.otherwise('/members'); }]) // メンバー情報を取得するためのGetMembersサービス .value('GetMembers', function() { return { '1' : { name: '佐藤理央', sex: '女', birth: new Date(2000, 5, 25) }, ...中略... }; });
ステートを入れ子にした場合のポイントは、以下の通りです。
(1)ステート名はドット区切りで表現
UI Routerでは、入れ子(階層)関係にあるステートをドット区切りで表すのが基本です。たとえば、この例であれば、members.detailsステートは、membersステートの子ステートであるという意味です。
(2)urlパラメーターは連結して解釈される
ステートが階層関係にある場合、urlパラメーターも「親のurl + 子のurl」と階層に即して連結されます。この例であればmembersステートのurlは「/members」、members.detailsステートのurlは「/:id」なので、members.detailsステートは、これらを連ねたもの「/members/:id」――たとえば「/members/13」のようなパスで呼び出せます。
(3)親テンプレートに子テンプレートの埋め込み先を準備する
ステートを階層関係にした場合、(デフォルトでは)子テンプレートもまた、親テンプレートの子供として埋め込まれます。よって、この場合も親テンプレートの中に子テンプレートの埋め込み先(ui-view属性)を準備しています。
また、ui-sref属性でリンク先を指定する場合、先頭に「.」を付けている点にも注目です。これはいわゆる相対パスを示すもので、(現在がmembersステートを示しているはずなので)members.detailsステートにリンクしなさい、という意味になります。「members.details({ id: id })」と書いても同じ意味です。
まとめ
以上、本稿ではngRouteモジュールによく似た構文で、より高機能なルーティングを提供するUI Routerについて解説しました。ルーティングは、複数のファイルが関連して動作するため、特に初学者にとっては混乱しやすいテーマでもあります。しかし、SPA(Single Page Application)を開発する上では、ルーティングは避けては通れないテーマです。一通りの理屈を理解した後は、配布サンプルをもとにページを拡張したり、パスを変えてみたりと手を動かしながら、理解を深めてみることをお勧めします。
次回は、ビュー開発をより便利にするフィルター集、Angular Filterについて解説の予定です。
参考資料
- 『AngularJSアプリケーションプログラミング』(技術評論社)
- 『AngularJSライブラリ 活用レシピ 厳選 108』(WINGSプロジェクト)