パスの指定とパスの遷移
パスの指定方法ですが、パスには完全一致以外にもパラメータを含めて記述ができます。例えば、今回のサンプルではリスト5のように:(コロン)+変数名で各タスクの識別用IDをパスに含めるようにしています。
$routeProvider.when('/item/:uid',{ // (省略) }
これ以外にも表2のような記述ができます。詳しい説明はAPIリファレンスを参照してください。
パターン | 例 | 説明 |
---|---|---|
:name | /item/:name | /item/book のように必ずnameに相当する値が必要。 |
:name? | /item/:name? | /itemもしくは/item/bookのいずれも一致。nameの指定が必ずしもいらない |
:name* | /category/:name* | /category/book/hobby のように/category/以下の一致するような指定が可能 |
パラメータで指定した変数は、リスト6のように$routeParamsサービスを利用して取得できます。
module.controller('ItemController',[ '$scope','$routeParams','$location','Items', // (1) $routeParamsサービスを利用する function($scope,$routeParams,$location,Items){ var uid = $routeParams['uid']; // (2) パスからパラメータの取得 /item/35 の場合には uid = "35"となる $scope.item = Items.get(uid); // (省略) }]);
(1)では、$routeParamsサービスをコントローラのDI機能を使って取得します。URLで指定したuidの値は取得した$routeParamsの配列形式として取得できます。
続いて、ここで指定したパスへの遷移は$locationサービスのpathメソッドを用いて遷移します。リスト7はタスクを登録が完了したら、一覧ページに遷移する場合の利用例です。
module.controller('AddController',['$scope','$location','Items','defaults', function($scope,$location,Items,defaults){ // (省略) $scope.addItem = function(){ if($scope.addItemForm.$valid) { Items.add($scope.item); $location.path("/list"); // pathを指定して画面遷移 } }; }]);
ただし、このようにコントローラ内で画面遷移をする場合には問題ないのですが、HTMLテンプレートにパスを記述する場合にAタグ等を使って記述したくなります。そのような場合には、アプリケーションがhashモードか、HTML5モードのどちらで動作しているかに注意する必要があります。そのためには、筆者はリスト8のようなディレクティブを作成して利用しています。ディレクティブの作成方法は以前の記事を参照してください。
routes.directive('routeHref',['$location',function($location){ return { restrict : 'A', scope : { 'routeHref' : '@' }, link : function($scope,$element,$attrs){ $element.on('click',function(){ $scope.$apply(function(){ $location.path($scope.routeHref); }); }); } } }]);
このようなディレクティブを作成することでHTMLテンプレート内では、リスト9のようにroute-href属性を使ってパスの指定が簡単に行えます。
<!-- (1) フッタでの一覧画面に遷移する際の記述 --> <a class="btn btn-default" route-href="/list"><span>一覧</span></a> <!-- (2) タスク一覧からの詳細画面に遷移する際の記述 --> <a class="list-group-item" ng-repeat="item in items" route-href="/item/{{item.uid}}"> <!-- 省略 --> </a>
画面遷移におけるイベント処理
SPAとしてアプリケーションを作成するとルート変更(画面遷移)のたびに実際にサーバにアクセスしているわけではないので、そのままでは一般的にアクセスログなどを取得することができません。従って、アプリケーション内で画面遷移が行われた時に各自でそのための処理をしなければなりません。ngRouteサービスでは画面遷移をする際に表3に示すイベントが発火されるのでそのイベントを利用して実現することができます。
イベント名 | 説明 |
---|---|
$routeChangeStart | ルート変更が行われる前のイベント。画面遷移を取り消すなどの処理が可能 |
$routeChangeSuccess | ルート変更が行われた後のイベント |
$routeUpdate | ルートが変更された時(ただし、when/otherwiseでのreloadOnSearchでfalseが指定されたとき) |
$routeChangeError | ルート変更でエラーが発生した時 |
リスト10はこのイベント処理を使った時のサンプルコードです。
routes.run(['$rootScope','$location',function($rootScope,$location){ // (1) ルート変更(画面遷移)が行われた時のイベント処理 $rootScope.$on('$routeChangeSuccess',function(event,cur,prev){ if(window.ga){ // 例えば、以下のようにしてAnalyticsでのPVとして処理する // window.ga('send', 'pageview', {page: $location.path()}); } }); // (2) ルート変更(画面遷移)が行われる前のイベント処理 $rootScope.$on('$routeChangeStart',function(event,next,cur){ if(!cur && $location.path().indexOf('/item') == 0){ event.preventDefault(); //(3)ルート変更を中止 $location.path('/list'); } }); }]);
(1)では、画面遷移が行われた時にGoogle Analyticsのページビューのトラッキングをすることを想定したコードです。このサンプルでは、トラッキングコードを入れていませんので機能しませんが、トラッキングコードとGoogle Analytics APIを参考に任意のトラッキング処理が行えます。(2)では、直接詳細ページにアクセスした場合、つまり、/item/35のようなパスに直接アクセスした際には一覧ページにリダイレクトする処理を記述しています。実際にルート変更を行わないようにするには(3)のようにpreventDefault()メソッドを使ってルート変更をキャンセルすることができます。
最後に
今回はSPAを構築する際にngRouteを利用しましたが、サードパーティのUI-Routerというものも存在し、こちらを利用されている方も多くいます。
UI-Routerはテンプレートの入れ子やルートに名前を指定できるなどngRouteよりも高機能になっています。ただし、ngRouteよりも少々複雑になっているのでSPAを初めて作成する場合には、ngRouteを利用し、その後、ngRouterで具体的な不都合が生じたら利用を検討するとよいでしょう。おおよそ同じような考え方で作成されているために非常に移行もしやすくなっています。
SPAでのWebアプリケーションが作れるようになると、これまでWebアプリケーションでは作れないと思っていたようなものも作れるようになりますので、これまでのWebアプリケーションとは一味違うアプリケーション作成にチャレンジしてください。