ビューを入れ子にする
さらに、UI Routerではビューを入れ子にすることもできます。たとえば、/membersではメンバーの一覧情報を表示し、一覧から特定のメンバーを表示すると、それぞれ/members/1、/members/7のようなパスで個別メンバーの情報が表示される、といったケースです。
このようなルートは、以下のように定義できます。
<!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プロジェクト)
