シングルページアプリケーションと URL 同期の課題
現代の Web 開発において、シングルページアプリケーション(SPA)は標準的なアーキテクチャとなっています。SPA では、ページ遷移ごとにサーバーから HTML を取得するのではなく、Ajax 通信によってデータのみを取得し、クライアント側で DOM を更新することで视图の切り替えを実現します。これにより、ユーザー体験は向上しますが、従来の Web アプリケーションとは異なる技術的な課題が生じます。
最も顕著な問題が、ブラウザの「戻る」ボタンやアドレス欄の URL が機能しなくなる点です。Ajax による通信はブラウザの履歴スタックに影響を与えないため、ユーザーがバックボタンを押しても前の状態に戻れないことがあります。この問題を解決し、URL とアプリケーションの状態を同期させる仕組みが「ルーティング」です。
一般的に、クライアントサイドのルーティングには以下の 2 つの手法が用いられます。
- Hash ベース: URL のハッシュフラグメント(#)の変更を監視し、
hashchangeイベントを利用して视图を切り替えます。 - HTML5 History API:
pushState()を使用して履歴を操作し、popstateイベントを監視して导航を行います。これにより、ハッシュ記号のない清潔な URL を実現できます。
これらの仕組みにより、アドレス欄の URL が変化してもページ全体をリロードせず、特定の视图のみを更新する「ディープリンク」が可能になります。
ngRoute モジュールの構成要素
AngularJS において、このルーティング機能は ngRoute モジュールによって提供されます。ブラウザの機能検知に基づき、Hash または History API を適切に使い分けることが可能です。ngRoute はコアモジュールとは分離されており、主に以下のコンポーネントで構成されています。
- $routeProvider: URL パスと视图テンプレート、コントローラーの映射を定義するサービスです。
- $routeParams: URL に含まれるパラメータ(例:
{id: 10})を格納するサービスです。 - $route: 現在のルート情報のマッチング処理を行い、関連する属性やイベントへのアクセスを提供します。
- ngView: メインの视图テンプレート内で、ルートに応じた子视图を挿入するためのディレクティブです。
これらに $location サービスを組み合わせることで、完全な SPA の导航システムを構築できます。
ルーティングの実装手順
1. モジュールの依存関係定義
ngRoute は別ファイルとして配布されているため、まず HTML ファイルでスクリプトを読み込む必要があります。その後、アプリケーションモジュールの定義時に依存関係として注入します。
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.5/angular-route.min.js"></script>
JavaScript 側では、以下のようにモジュールを宣言します。
var app = angular.module('InventoryApp', ['ngRoute']);
これにより、アプリケーション内でルーティング関連のサービスおよびディレクティブが利用可能になります。
2. ルートテーブルの定義
路由の設定は、$routeProvider を使用して行います。主なメソッドは when(path, route) と otherwise(params) です。
when メソッドの第一引数 path は、ブラウザのアドレス欄($location.path())とマッチする文字列です。パラメータを含む場合は、コロン followed by 名前(例:/items/:itemId)で定義します。これにより、URL /items/500 にアクセスした際、パラメータ {itemId: '500'} が $routeParams に格納されます。また、ワイルドカード(*)を用いた柔軟なマッチングも可能です。
第二引数の route オブジェクトでは、マッチ時の動作を定義します。主な設定項目は以下の通りです。
controller // 视图に紐付けるコントローラー(関数または文字列)
controllerAs // コントローラーのエイリアス名
template // インラインで定義する HTML テンプレート
templateUrl // 外部 HTML ファイルまたは ng-template のパス
resolve // コントローラー実行前に解決すべき依存関係
redirectTo // 特定条件下でのリダイレクト先
具体的な設定例を以下に示します。ここでは商品一覧と詳細画面の路由を定義しています。
app.config(function($routeProvider) {
$routeProvider.
when('/products', {
controller: 'ItemListController',
templateUrl: 'views/product-list.html'
}).
when('/details/:itemId', {
controller: 'ItemDetailController',
templateUrl: 'views/product-detail.html'
}).
otherwise({
redirectTo: '/products'
});
});
otherwise メソッドは、どのルート定義にもマッチしなかった場合に適用されるデフォルトの動作を指定します。上記の例では、存在しないパスへアクセスされた場合にトップページへリダイレクトします。
3. 视图挿入箇所の指定
SPA における「部分更新」の領域を指定するには、ngView ディレクティブを使用します。このディレクティブが配置された場所が、ルート変更に応じて動的に書き換えられる領域となります。
要素としての使用法:
<ng-view></ng-view>
属性としての使用法:
<div ng-view></div>
この設定を行うことで、URL の変化に応じて定義されたテンプレートとコントローラーが自動的に読み込まれ、视图が更新される仕組みが完成します。