https://github.com/angular-ui/ui-router/wiki のおおざっぱな訳と公式の記事に少しだけサンプルを追加しました。
記事内のリンク先は、直接元コンテンツの URL に飛びます。(いずれ訳したい)
このガイドは UI-Router のコンポーネントとそのオプションへ深く関わるための記事です。もし、クイックリファレンスガイドを見たい場合は API Reference を参照してください。
State Manager
angular-ui router の提供する $stateProvider は AngularJS v1 のルータを元に、純粋な状態(ステート)へ注力して動作します。
- 状態は、全体的なUIとナビゲーションの観点から、アプリケーション内の「場所」に対応しています。
- 状態は、(コントローラ/テンプレート/ビューのプロパティを介して)UIがその場所でどのように見え、動作するかを記述します。
- 状態は、多くの場合に共通点を持っており、このようなモデルでこれらの共通点がもたらす物は、状態の階層、すなわち親/子の状態、ネストされた状態である。(翻訳自信なし)
一番シンプルな書き方
<body ng-controller="MainCtrl">
<section ui-view></section>
</body>
angular.module('sample', ['ui.router']).config([$stateProvider, function($stateProvider) {
$stateProvider.state('contacts', {
template: '<h1>My Contacts</h1>'
});
}]);
テンプレートはどこに挿入されるのか
state が作動した場合、親の state のテンプレート内の ui-view
アトリビュートが設定されたエレメントに挿入される。上記のように親のテンプレートが存在しない場合、 index.html が親のテンプレートとして扱われる。
state を作動させる
上記のように contacts の state を作動させるためには、以下の方法がある。
-
$state.get()
メソッドを呼ぶex.
$state.get('contacts')
-
ui-sref
ディレクティブを付けた要素をクリックする (ex)ex.
<a ui-sref="contacts">Go contacts</a>
-
URL を用いて、 state にアクセスする
ex.
http://example.com/#/contacts
Template について
テンプレートを設定する方法はいくつかある。
一番シンプルな方法は template
プロパティに String として指定する。
$stateProvider.state('contacts', {
template: '<h1>My Contacts</h1>'
});
大体は、以下のように分割された html ファイルへのパスを templateUrl
プロパティへ指定する。
$stateProvider.state('contacts', {
templateUrl: 'contacts.html'
});
templateUrl
プロパティは、関数としても指定できる。その際、関数に stateParams
という引数を取ることができる。(これはインジェクトされた引数ではない)
$stateProvider.state('contacts', {
templateUrl: function(stateParams) {
return 'partials/contacts.' + stateParams.filterBy + '.html';
}
});
もしくは、インジェクトすることができる templateProvider
プロパティを使って以下のようにテンプレートを返すことができる。
$stateProvider.state('contacts', {
templateProvider: function($timeout, $stateParams) {
return $timeout(function() {
return '<h1>' + $stateParams.contactId + '</h1>';
}, 100);
}
});
ui-view
ディレクティブ内にはデフォルトのコンテンツを持つことができ、 state が作動した場合に置換される。この場合、 state の管理外になったときはデフォルトのコンテンツが再び挿入される。
<body>
<ui-view>
<i>ここのコンテンツが表示される</i>
</ui-view>
</body>
Controller について
テンプレートには1つのコントローラを指定できる 注意 指定されたコントローラーは、テンプレートが指定されていない場合にはインスタンス化されない。
コントローラは以下のように controller
プロパティへ指定する。
$stateProvider.state('contacts', {
template: '<h1>{{title}}</h1>',
controller: function($scope){
$scope.title = 'My Contacts';
}
})
また、モジュール内に定義済みのコントローラ名を指定することもできる。
$stateProvider.state('contacts', {
template: '<h1>{{title}}</h1>',
controller: 'ContactsController'
})
また、コントローラ名は Controller As Syntax でも指定することができる。
$stateProvider.state('contacts', {
template: '<h1>{{contact.title}}</h1>',
controller: 'ContactsCtrl as contact'
})
さらに進んだ使い方をする場合、controllerProvider
を用いて動的にコントローラの関数や名前を指定することができる。
$stateProvider.state('contacts', {
template: ...,
controllerProvider: function($stateParams) {
var ctrlName = $stateParams.type + "Controller";
return ctrlName;
}
})
コントローラ内では $scope.on()
を用いることで state の遷移をイベントを監視することができる。
コントローラは必要になった場合にインスタンス化され、スコープが生成される。すなわち、ユーザが URL を用いて state を作動させた場合、 $stateProvider
は必要なテンプレートをビューにロードし、テンプレートのスコープにコントローラを紐付ける。
Resolve について
コントローラと一緒にコンテンツや state のためにカスタマイズしたデータを準備する場合は resolve
プロパティを使用する。
resolve
プロパティは、コントローラにインジェクトするべきものの依存関係マップを指定するオプションである。
プロミスで扱ういくつかの依存関係は、コントローラがインスタンス化される前、また $routeChangeSuccess
イベントが発火する前にその解決や変換が行われる。
resolve
プロパティは、オブジェクトでマッピングしていく。そのマッピングオブジェクトは、以下のような key/value のペアで指定する。
- key - {String}: コントローラにインジェクトされる名前を指定
- factory - {String|Function}:
- String で指定した場合、サービスへのエイリアスとして扱われる
- Function で指定した場合、その関数はインジェクトされて戻り値が依存した値となる。戻り値がプロミスだった場合、それはコントローラがインスタンス化される前に解決され、コントローラにインジェクトされる
例
resolve
内のオブジェクトは、コントローラがインスタンス化される前に必ず解決されなければならない。(プロミスの場合は deferred.resolve()
を通っていること)
resolve
がどのようなパラメータとしてコントローラへインジェクトされるかを注意すること。
$stateProvider.state('myState', {
resolve:{
// 関数を使って値を返す一番簡単な例
// プロミスを使っていないので、即座に値が解決される
simpleObj: function(){
return { value: 'simple!' };
},
// 関数を使ってプロミスを返す例
// resolve が使われる典型的なケースとして、$http を用いてサービスをインジェクトしたい場合を例とする
promiseObj: function($http){
// $http は url に対するデータのためのプロミスを返す
return $http({method: 'GET', url: '/someUrl'});
},
// プロミスを使った他の典型的な例として
// 結果のデータを .then メソッドを用いて加工したい場合は以下のようになる
promiseObj2: function($http){
return $http({method: 'GET', url: '/someUrl'})
.then (function (data) {
return doSomething(data);
});
},
// String でサービスの名前を指定する例
// 以下では、モジュール内の 'translations' というサービスを返す
// Note. サービスがプロミスを返した場合は、上記の関数でプロミスを返す場合と同様に動作する
translations: "translations",
// Example showing injection of service into
// resolve function. Service then returns a
// promise. Tip: Inject $stateParams to get
// access to url parameters.
// 関数へインジェクションを行い、サービスを使う例
// 例ではサービスはプロミスを返している
// Tip. URL のパラメータにアクセスするために $stateParams をインジェクトすることができる
translations2: function(translations, $stateParams){
// getLang メソッドは、$http を用いて "/:lang/home" のような URL をリクエストするような状況を想定している
return translations.getLang($stateParams.lang);
},
// 自分自身で生成したプロミスを使用する例
greeting: function($q, $timeout){
var deferred = $q.defer();
$timeout(function() {
deferred.resolve('Hello!');
}, 1000);
return deferred.promise;
}
},
// コントローラは resolve に指定されたものの完了を待ってからインスタンス化される
// 例としてあげるなら、コントローラは promiseObj のプロミスが完了されるまでインスタンス化されない、ということ
// 完了した段階で、 resolve に指定した物はコントローラにインジェクトされ使用することができる
controller: function($scope, simpleObj, promiseObj, promiseObj2, translations, translations2, greeting){
$scope.simple = simpleObj.value;
// promiseObj は使用可能になっている
$scope.items = promiseObj.items;
$scope.items = promiseObj2.items;
$scope.title = translations.getLang("english").title;
$scope.title = translations2.title;
$scope.greeting = greeting;
}
});
子のステートで使用する場合の resolve などの詳細はここ
state にカスタムデータを渡す
state を設定するオブジェクトには、データを渡すことができる。
※渡す場合は他のプロパティとのコンフリクトを避けるため data
プロパティでの設定をおすすめする
// 例では、オブジェクトを渡して state を設定する方法と string で指定する方法を記す
var contacts = {
name: 'contacts',
templateUrl: 'contacts.html',
data: {
customData1: 5,
customData2: "blue"
}
};
$stateProvider
.state(contacts)
.state('contacts.list', {
templateUrl: 'contacts.list.html',
data: {
customData1: 44,
customData2: "red"
}
});
上記のように state を設定した場合、コントローラからは以下のようにアクセスすることができる
function Ctrl($state){
console.log($state.current.data.customData1) // outputs 5;
console.log($state.current.data.customData2) // outputs "blue";
}
子のステートで使用する場合の data プロパティなどの詳細はここ
onEnter, onExit コールバックについて
state にはオプションとして onEnter
onExit
のプロパティを用いて、それぞれ state のアクティブ、非アクティブのタイミングでコールバックを呼ぶことができる。コールバックは、resolve で設定した依存物にアクセスすることができる。
$stateProvider.state("contacts", {
template: '<h1>{{title}}</h1>',
resolve: { title: 'My Contacts' },
controller: function($scope, title){
$scope.title = 'My Contacts';
},
onEnter: function(title){
if(title){ ... do something ... }
},
onExit: function(title){
if(title){ ... do something ... }
}
});
State Change Event について
これらのイベントは全て $rootScope
で発火する
- $stateChangeStart - 遷移が始まったときに発火する
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams){ ... });
Note: event.preventDefault()
を使うと何らかの出来事のために遷移を止めることができる
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams){
event.preventDefault();
// transitionTo() のプロミスは 'transition prevented' エラーを持ってリジェクトされる
});
-
$stateNotFound -
v0.3.0
- 遷移するべき state が存在しない場合に発火する。このイベントは任意のハンドラが(見つからない state を遅延ロードすることによって)エラーに対処するための唯一の方法である。ハンドラに渡されるunfoundState
オブジェクトは、下記のサンプルにて参照のこと。event.preventDefault()
を使うことによって、遷移を中断させることができる(その場合に transitionTo() プロミスは 'transition abort' イベントをもってリジェクトされる) その他、遅延ロードによる state のロードなどはこちらを参照のこと。
// lazy.state が定義されていないとする
$state.go("lazy.state", {a:1, b:2}, {inherit:false});
// ハンドラの処理
$rootScope.$on('$stateNotFound',
function(event, unfoundState, fromState, fromParams){
console.log(unfoundState.to); // "lazy.state"
console.log(unfoundState.toParams); // {a:1, b:2}
console.log(unfoundState.options); // {inherit:false} + default options
});
- $stateChangeSuccess - 遷移が完了した際に発火する
$rootScope.$on('$stateChangeSuccess',
function(event, toState, toParams, fromState, fromParams){ ... })
-
$stateChangeError - 遷移の際に何らかのエラーが発生した場合に発火する。 注意
resolve
内の関数で発生した様々なエラーは伝統に従って throw されないので、必ず$stateChangeError
のハンドラで catch しなければならない。
$rootScope.$on('$stateChangeError',
function(event, toState, toParams, fromState, fromParams, error){ ... })
View Load Event について
-
$viewContentLoading - ビューがローディングされ、DOM がレンダリングされる前に発火される。このイベントは
$scope
に対して発火する。
$scope.$on('$viewContentLoading',
function(event, viewConfig){
// ここではビューの全てのコンフィグプロパティにアクセスすることができる
// また 'targetView' プロパティという特別なものが提供される
// viewConfig.targetView
});
-
$viewContentLoaded - ビューがローディングされ、DOM がレンダリングされたあとに発火する。このイベントは
$scope
に対して発火する。
$scope.$on('$viewContentLoaded',
function(event){ ... });
おかしい内容があればコメントか damele0n@twitter まで。