Easier AngularJS Routing with Angular UI Router

AngularJS には組み込みルーティングがありますが、時にはそれが制限されていると感じることがあるかもしれません。 AngularJS のネイティブ ルーティング実装は、アプリケーション ルーティングに一致するコントローラを初期化する役割を果たします。 この機能は基本的なシナリオではうまく機能しますが、高度な状況では、Angular のネイティブ ルーティングが次のようなものであることにすぐに気づきます。

  • URL が変更されたときにコード ベース全体で URL 文字列を手動で変更する必要がある。
  • ネストされたビューを提供しない。
  • 名前付きビューを提供しない。
  • ナビゲーション中にデータを渡すことができない。

代替手段として、Angular UI Router フレームワークはルーティングの抽象化レイヤーで、ナビゲーションにより明確なアプローチを特徴とします。 UI ルーター フレームワークはまた、ネストされたビューと名前付きビューを提供することにより、ネイティブ実装のギャップをいくつか埋め、ビュー間でデータを渡すこと、およびその他多くを可能にします。

Angular UI Router フレームワークは、ナビゲーションへの宣言的なアプローチを特徴とするルーティングの抽象化レイヤーです

この記事では、UI Router フレームワークを使用するシンプルなアプリケーションを構築することを学習します。 その過程で、状態、依存関係を解決する方法、およびナビゲーションのさまざまなメソッドに精通することになります。

Understanding States

おそらく、UI ルーター フレームワークが解決を望む問題を理解する最善の方法は、Web の本質を考慮することです。 ほとんどのアプリケーションでは、ページへのリンクを作成するとき、明示的な URL パスを定義します。 たとえば、サイトの製品ページに移動したい場合、

Code Sample 1: Version 2 Flexible Container

http://www.example.com/products

のような URL があるかもしれません。この状況には 2 つの問題があります。 1つ目は、リンクを設定するたびに、Products ページへの正確なリテラル パスを覚えておかなければならないことです。 ここで挙げた例は簡単に思い出せるかもしれませんが、現実の多くのURLはそう簡単に記憶できるものではありません。 次に、誰かがパスを別のものに変えてしまうという不可避の事態が発生した場合に問題が生じます。 URL が変更された場合、すべての既存のリンクが新しい場所を指すように更新されていることを確認する必要があります。

URL パスを逐一追跡する代わりに、アプリケーションに「製品ページに移動」と指示するだけではどうでしょうか。 アプリケーションがナビゲーションに関与できるようにすることで、文字通りのパスを知る必要から解放され、変更の必然性によるリンク切れから保護されます。 ステートを使用することで、このような柔軟性が得られます。 状態は、URL の場所、状態の名前、ビューのための特別なデータ、ビューを検索または生成する方法の識別をカプセル化し、カスタム イベントを公開することさえできます。 UI ルーターは、アプリケーションが動的コンテンツのプレースホルダーを保持するシェルで構成される点で AngularJS のネイティブ ルーティングと非常によく似ています。 図1は、アプリケーションシェルがui-viewディレクティブを使用する要素をホストしている様子を示しています。

Figure 1: UI Router framework adds HTML content into a placeholder on the page.

Angular UI Router is a framework that wholly replaces the native routing available in AngularJS.The UI Router framework offers the placeholder in the page.

HTML コンテンツをレンダリングするだけでなく、UI ルーター フレームワークは、URL ルーティング、コントローラーが初期化される前に依存関係を解決する機能、名前付きおよび入れ子のビュー、ユーティリティ フィルター、ステート変更イベント、およびステート間の宣言的な遷移をサポートします。 最初の方法は、ui-sref ディレクティブです。 HTML のアンカー タグ (ハイパーテキストの参照を表す) の href 属性に慣れていると思いますが、同じように、ui-sref 指令は状態の参照を表します。 このディレクティブを使用するには、アンカーに ui-sref ディレクティブを適用したステート名を宣言します。 例えば、

<a ui-sref="about">About Us</a>

このディレクティブをUI Routerフレームワークが評価すると、アンカーは適切なURL値を持つように変換される。 例えば、

<a ui-sref="about" href="#about">About Us</a>

About Us ページに移動するためにどのように URL が更新されなければならないかに対応する値を持つ href 属性を含むように要素が更新されることに注意してください。 ui-sref ディレクティブは非常に柔軟です。

状態間を移動するための次のアプローチは、Angular コントローラーが使用できる $state オブジェクトのメソッドを使用することです。

angular.module('app') .controller('PageController', );

$state オブジェクトは UI ルーター フレームワークによって注入され、アプリケーションの状態を管理および操作するための多数のメソッドを含んでいます。 ここでの価値は、アプリケーションに about state に「行く」ように指示することであり、ページへのリテラル URL パスを知ることから解放されます。

Downloading and Installation

ユーザーが UI ルーター フレームワークにアクセスする方法はさまざまあります。 最新版を https://github.com/angular-ui/ui-router の GitHub リポジトリから直接ダウンロードすることができます。 あるいは、Bower または NuGet を介してフレームワークをインストールしたり、ページに CDN リンクを含めたりすることも可能です。

Using Angular UI Router

以下は、UI ルーター フレームワークを使用して、単純な静的コンテンツ ベースのアプリケーションを構築する方法を示すチュートリアルです。 図 2 は、この記事を読みながら構築することを学ぶ、ホームページのサンプルアプリケーションを表しています。 このスクリーンショットでは、アプリケーションのシェルと、ui-viewディレクティブを使用してホームページのコンテンツがプレースホルダに注入される様子を見ることができます。

図 2 : ホームページとアプリケーションのデフォルト状態

図 3 に示すように、状態を変更してコンタクト ページに移動することができます。 コンタクトページでの仕組みは、$stateオブジェクトのgoメソッドにステート名を渡して使用します。

Figure 3 : The contact page

次の状態は、図4に見られるように記事のリストページと関連しています。 ここでは、記事データの配列は、UIフレームワークによってコントローラに生の値が注入された後、ビューで利用できるようになります。 このページでのナビゲーションは、ナビゲーションしたいアプリケーションの状態を宣言的に表現できる ui-sref ディレクティブによって促進されます。

図 4 : 記事リストページ

図 5 で示される最後のページは、アプリケーションで入れ子になった状態が使用される方法を示しています。

Figure 5 : The article detail page

Configuration

UI Router フレームワークで作業を開始するには、ページを構成する必要があります。 最初のステップは、ページ上の HTML 要素の ng-app 属性にアプリケーション名を追加することです。 ここでは、アプリケーション名は単に app.

< html ng-app="app">

次に、フレームワークによって注入されるコンテンツのプレースホルダーとして動作するように、ページ上の要素に ui-view ディレクティブを追加する必要があります。 この例では、ディレクティブは div 要素に追加されます。

< div ui-view></div>

最後に、ページ上で Angular と Angular UI Router の両方を参照する必要があります。

<script src="scripts/lib/angular.min.js"></script><script src="scripts/lib/angular-ui-router.min.js"></script><script src="scripts/app/app.js"></script>

このコード スニペットには、Angular アプリケーションを初期化するコードを保持する script/app フォルダーの app.js スクリプトへの参照も含まれています。 初期化プロセスは、UI ルーター フレームワークの設定とインターフェイスの大部分が実装される場所です。

Defining States

前述のように、UI ルーター フレームワークの基本は、アプリケーションで異なる状態を使用することです。 これらの状態のそれぞれにアクセスすることにより、アプリケーションは、アプリケーションのライフサイクル内の状況に移動したり、それ自体を再構成したりできます。 以下のセクションでは、アプリケーションの状態を定義する方法を説明します。すべてのコードは app.js ファイルに実装されています。 各セクションを個別に検証しますが、完全な初期化スクリプトを見たい場合は、リスト 1 を参照してください。

最初のステップは、AngularJS アプリケーションで UI Router を構成することです。 モジュールに名前を付けた後、ui.router というリテラルを依存関係の配列に追加することによって、UI Router フレームワークをアプリケーションの依存関係として登録する機会があります。 (コメントが後続のスニペットのコードのプレースホルダーを示すことに注意してください。)

angular.module('app', ) .config(/* add configuration here */);

モジュールを定義し依存関係を登録すると、アプリケーションの構成フェーズで実行される匿名関数をセットアップすることができます。 ここでは、UI ルーター フレームワークに関連するいくつかのリソースが関数に注入されています。

 

$stateProvider オブジェクトは、URL の変更と一致するかしないかを問わず、粒状のアプリケーション状態を定義できる state メソッドを備えています。 urlRouterProvider は、ブラウザの位置を管理および観察する方法を制御できるオブジェクトです。 UIルータのコンテキストでは、$urlRouterProviderは、キャッチオールナビゲーションシナリオを定義するのに役立つために使用されます。 これらのオブジェクトのそれぞれについて、今後のコード・スニペットでより詳しく説明します。 (再度、後続のコード スニペットは前のスニペットでプレースホルダー コメントの位置に配置されることに注意してください。)

各アプリケーションの状態は、名前を提供して、ビュー用のマークアップを見つける場所をフレームワークに伝えることによって定義されます。

$stateProvider .state('home', { url: '/', templateUrl: '/partials/home.html' })

これは、ユーザーがアプリケーションのルートに移動したときに、home.html ファイルのコンテンツを ui-view プレースホルダにロードするようにアプリケーションに指示します。 ここで、状態中心ルーティングの利点の 1 つを理解することができます。 もし何らかの理由で、ホームステートの URL をベアルートの場所 (/) ではなく /home を指すようにしたい場合、その変更は設定のここでだけ行われればよいのです。 この状態では、高度な設定は行われず、静的なページがブラウザに読み込まれます。

Contact ステートは、contact.html ページのマークアップを ui-view プレースホルダに読み込むように設定されています。 基本的な置換操作を行うだけでなく、ContactsController は ui-view ディレクティブをホストする DOM 要素のレベルでスコープされたビューにも関連付けられます。

 .state('contact', { url: '/contact', templateUrl: '/partials/contact.html', controller: 'ContactController', })

図 3 に示すように、Contact ページには Articles ページに移動するためのボタンが含まれています。 ナビゲーションは ContactsController で行われ、UI Router フレームワークによって必要に応じてロードされるビューにコントローラを配線する方法を示しています。

Article ページのステートでは、オブジェクトに定義された任意の構成された値を解決する値を追加することにより、構成をさらに一歩進めています。 この状態の目的は、サイト上で利用可能な記事のリストをレンダリングすることです。 このステートは、コントローラがインスタンス化される前に、記事の情報を利用できるように設定されています。 次のスニペットでは、状態が resolve オブジェクトの値を定義しています。

 .state('articles', { url: '/articles', templateUrl: '/partials/articles.html', resolve: { articles: 'ArticlesService' }, controller: 'ArticlesController' })

この場合、articles プロパティは文字列 ArticlesService を指しています。 resolve プロパティに値として文字列を渡すと、フレームワークは同じ名前で登録されているサービスにコンタクトし、最終的な値まで解決します。 この場合、ArticlesServiceはプロミスを返すので、サービスのプロミスが解決されて最終的なオブジェクトがコントローラに注入可能な値として利用できるようになるまで、関連するコントローラはインスタンス化されません。 ArticlesService の実装は、リスト 3 にあります。

図 4 で描かれているように、記事のリストがユーザーに表示された後、ユーザーは記事を選択し、サイトのコンテンツにドリルインすることができます。 このアクションは、ネストされた状態によって表されます。 状態名には、状態の間の親と子の関係を示すために、articles と article の間にドット (.) が含まれていることに注意してください。 これはネストされたビューであるため(状態名のドットで示される)、urlプロパティの値は、親状態のurl値と結合されます。 これは、一致する状態はすべて、/articles で始まり、記事のページ名を含む URL を持つことを意味します。

コロン (:) があることは、URL パラメータであることを意味します。 URL にパラメータを導入することにより、状態の定義は、その親状態との関係に一致する任意の状態を処理するのに十分な柔軟性を持つようになります。 このステートは、templateUrlの値を返すために実行される関数も特徴です。 ここで関数を使用すると、ステートのURLで定義されたパラメータを使用する機会が得られます。 urlプロパティでパラメータにどのような名前を付けても、$stateParamsオブジェクトのプロパティ名と一致します。 したがって、この状態は、URL で渡された pageName を受け取り、templateUrl 関数で使用して、最終的に ui-view ディレクティブをホストする要素に注入される個々のコンテンツ ファイルにアクセスします。

これが、アプリケーションで定義された最後の状態です。 実際の初期化スクリプトですべての状態がどのように実装されるかを見るには、リスト 1 を参照してください。

アプリケーションに与えるために必要な最後のコマンドは、ユーザーが configure メソッドで定義されていない URL にアクセスしようとした場合に何を行うかです。 urlRouterProvider オブジェクトから otherwise メソッドを使用することにより、認識されない URL はすべて破棄され、アプリケーションはデフォルトの場所にリダイレクトされます。 この例では、与えられた URL が定義された状態に一致しない場合、アプリケーションはルート URL にリダイレクトされるように構成されます。

$urlRouterProvider.otherwise('/');

さて、各アプリケーションの状態を定義したので、ArticlesService の構築に目を向け始めることができます。

Resolving Data with the Articles Service

記事の状態に対する構成には resolve オプションに対する値も含まれています。 このオブジェクトは、ArticlesService という文字列値が articles プロパティーに設定されるように構成されています (コンテキストについては、リスト 1 を参照してください)。 resolve オブジェクトに文字列を提供することは、アプリケーションに登録されたサービスを探し出し、そのサービスを最終的な値まで解決するようにフレームワークに指示します。 ArticlesServiceはプロミスを返すように実装されています。

angular.module('app').factory('ArticlesService', ); return deferred.promise; }]);

ここでは、$qサービスを使用して、配列を返すプロミスを作成しています。 この例では、値はハードコードされていますが、実際のコンテキストでは、データを提供するためにリモートサーバーにアクセスする必要がある場合があります。 いずれにせよ、ルータフレームワークが関連するコントローラに実行を渡す前に、サービスを完全に解決しておく必要があります。 したがって、記事の状態が呼び出されると、最終的にコントローラーは依存関係として記事オブジェクトの配列を渡されます。

Using Resolved Data in the ArticlesController

UI ルーター フレームワークを使用する利点の 1 つは、関心の分離を強化する機能です。 articles ステートが resolve オブジェクトを実装しているので、article の生の配列がコントローラーに注入されます。

angular.module('app') .controller('ArticlesController', );

このアプローチは ArticlesController に ArticlesService について「知る」ことを要求するよりも優れています。 アプリケーションのコントローラーに対する完全な実装は、リスト 2 にあります。

Rendering the Articles List

さて、アプリケーションが記事の状態にナビゲートし、コントローラーが解決済みの記事配列をスコープに設定すると、今度はビューをレンダリングする準備が整いました。 Articles ビューは 2 つの部分から構成されています。 1 つ目は、入れ子になったビューを作成するために ui-view ディレクティブを使用した別の div プレースホルダーです。 もうひとつは、サイト上で利用可能なさまざまな記事を並べ替えたリストです。 このようにビューを構築することで、記事のリストをページに残したまま、異なる記事のタイトルをクリックすることができます。 (これは、ページの内容が記事レベルのui-viewに読み込まれ、ページ全体がアプリケーションシェルのui-viewにレンダリングされるためです(図5参照)。 アプリケーション シェルの完全な実装は、リスト 4 にあります。

Article ビューが入れ子のビューを実装する方法を次のコード スニペットで説明します。 まず、div 要素は ui-view ディレクティブをプレースホルダーとして使用し、コメントにあるように、フレームワークによってコンテンツがレンダリングされる前に、プレースホルダーでレンダリングするデフォルトのコンテンツを渡すことができます。 リスト 5 は、静的なメッセージが、ビューにコンテンツが読み込まれる前にページ上のプレースホルダー コンテンツとして使用される方法を示しています。

第二に、アンカー要素に ui-sref ディレクティブが適用されていることです。 これは、フレームワークのコンテキストでこのリンクを処理するように UI ルーター フレームワークに信号を送り、最終的に、アプリケーション構成で定義された設定に基づいて宣言された状態の URL に一致する標準の href 値をレンダリングします (リスト 1 を参照)。

3 番目のフレームワークの使用方法は、ui-sref ディレクティブの値が、ネストされた状態の正しい URL 値を生成するための式を受け入れるというものです。 ここでは、ハッシュが入れ子状態の階層 (この場合は articles.article) に渡され、pageName の値は入ってくる記事の pageName に束縛されます。 UI Router フレームワークがこの式を評価すると、定義された状態の規則に一致する各記事について、対応する URL 値が生成されます。

More Abstract Navigation

最後に実装するコントローラは ContactController で、これは state パラメータの go メソッドを使用してアプリケーションを新しい状態に移動させます。

app.controller('ContactController', );

ここで、単に状態名で go を呼び出すことにより、コントローラーは、アプリケーションの具体的なルーティング スキームを追跡するのではなく、変更したい状態を宣言することにのみ関心を持ちます。 UI ルーター フレームワークは、状態の定義、依存関係の解決、およびネストされたビューの使用を行うための簡単な方法を提供します。 このフレームワークでできることの詳細については、GitHub の https://github.com/angular-ui/ui-router/ にあるこのプロジェクトのホームを参照してください。

コメントを残す

メールアドレスが公開されることはありません。