はじめに
Angularの1系2系両方に対応した新しいrouterとして開発が進められているNew Routerをさわってみました。
ドキュメントやサンプルがまだ整備中ということもあり、何が必須の要素で何が必須ではないのか読み取りにくかったので、実際にコードを足したり消したりで重箱の隅をつついてみました。
ドキュメントではまだ説明されていないnested routeも試してみます。
対象バージョンは執筆時点で最新の0.5.3、AngularJS本体は1.3.15とします。
Route設定
routeの設定をするために2通りの方法が提供されています。
1つは、controllerの$routeConfig
プロパティで設定するパターン、もう1つはcontrollerにDIした$router
で設定するパターンです。
これらの違いを説明する前に1点確認なのですが、New Routerではcontrollerはcontroller asでインスタンス化されて使用します。
このあたりのルールは既に @Quramy さん、albatrosaryさんがまとめてくれています。
- http://qiita.com/Quramy/items/9a2ea71241b261028b6f
- http://albatrosary.hateblo.jp/entry/2015/03/30/075952
$routeConfig
さて、$routeConfig
プロパティを使ったrouteの設定です。
以下のように、ngNewRouterを読み込み、controllerを定義、そしてcontrollerの$routeConfig
プロパティを設定しています(ngNewRouterはいずれ違う名前になるんじゃないかと思われます)。
(function () {
'use strict';
angular.module('hoge', ['ngNewRouter'])
.controller('AppController', RootController);
function RootController() {
}
RootController.$routeConfig = [
{
path: '/',
redirectTo: '/home'
},
{
path: '/home',
component: 'home'
}
];
})();
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>router</title>
</head>
<body ng-app="hoge" ng-controller="AppController as root">
<div ng-viewport></div>
<script src="angular.js"></script>
<script src="router.es5.js"></script>
<script src="app.js"></script>
</body>
</html>
サンプルでは、controller定義がグローバルスコープに漏れ出ていたのが気になったので即時関数で包んでみましたが特に問題はありませんでした。
JavaScript上の関数名とAngularに伝えるcontroller名をあえて変更してみました。
今現在は、ルーティング最上位のcontroller名はAppControllerにしないといけないという制約があるそうです。
JavaScript上での関数名やcontroller asで受けるインスタンス名に制約はなさそうです。
この制約はバージョン0.6で解消予定だそうです。
ドキュメントやサンプルを見ると、$routeConfig
でルート設定を行った上、controllerに$router
をDIしている例が見られますが、$router
をDIする必要はとくになさそうです。
$router.config
今度は$routerを使ったroute設定です。
(function () {
'use strict';
angular.module('hoge', ['ngNewRouter'])
.controller('AppController', RootController);
function RootController($router) {
$router.config([
{
path: '/',
redirectTo: '/home'
},
{
path: '/home',
component: 'home'
}
]);
}
})();
$router
をDIしてconfig関数を呼んでいるだけで、それほど大きな違いはありませんね。
こちらの場合、$routeConfig
のようなcontroller名の制約はありませんでした。
controllerにあれこれDIして使うことを考えると、$routeConfig
を使ったほうがコードはすっきりしそうです。
ちなみに$routeConfig
と$router.config
両方使った場合は両方とも採用されるようですが、pathが重複した場合はエラーが出ます。
componentの定義
ルーティングされるcomponentは以下のように記述します。
(function () {
'use strict';
angular.module('hoge')
.controller('HomeController', HomeController);
function HomeController() {
this.header = 'ハロー!';
}
})();
<h1>{{home.header}}</h1>
サンプルでは、componentをangular.module('hoge.home', [])
のように別モジュールに分けていましたが、分けなくても特に問題はないようです。
さらに、index.htmlにscriptタグを追加します。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>router</title>
</head>
<body ng-app="hoge" ng-controller="AppController as root">
<div ng-viewport></div>
<script src="angular.js"></script>
<script src="router.es5.js"></script>
<script src="app.js"></script>
<script src="components/home/home.js"></script>
</body>
</html>
jsファイルをcomponents/componentName/componentName.jsに配置していますが、jsファイルの配置場所に関するルールはなくController名だけルールに従っていれば大丈夫です。
そのためビルドパイプラインを通して単一のjsファイルにまとめたりしても特に問題はないでしょう。
また、component名とcontroller、templateの対応ルールは$componentLoader
で変更可能だそうです。
nested route
/homeの下のパスに/home/sub1、/home/sub2のように要素を追加してみます。
まず、HomeComponentを以下のように変更します。
(function () {
'use strict';
angular.module('hoge')
.controller('HomeController', HomeController);
function HomeController() {
this.header = 'ハロー!';
}
HomeController.$routeConfig = [
{
path: '/sub1',
components: {
sub: 'sub1'
},
as: 'sub1'
},
{
path: '/sub2',
components: {
sub: 'sub2'
},
as: 'sub2'
}
];
})();
<h1>{{home.header}}</h1>
<a ng-link="sub1">sub1</a>
<a ng-link="sub2">sub2</a>
<div ng-viewport="sub"></div>
HomeControllerに更にルーティング情報を追加し、HTMLに名前付きng-viewportを追加し子componentの表示場所を確保しています。
また、ng-linkでサブcomponentに遷移させるために、$routeConfig
のasでrouteにaliasをつけています。
次にSub1ComponentとSub2Componentの定義です。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>router</title>
</head>
<body ng-app="hoge" ng-controller="AppController as root">
<div ng-viewport></div>
<script src="angular.js"></script>
<script src="router.es5.js"></script>
<script src="app.js"></script>
<script src="components/home/home.js"></script>
<script src="components/sub1/sub2.js"></script>
<script src="components/sub2/sub2.js"></script>
</body>
</html>
(function() {
angular.module('hoge')
.controller('Sub1Controller', Sub1Controller);
function Sub1Controller() {
}
})();
<h2>sub1</h2>
(function() {
angular.module('hoge')
.controller('Sub2Controller', Sub2Controller);
function Sub2Controller() {
}
})();
<h2>sub2</h2>
子componentもcomponentsディレクトリにフラットに配置すればよさそうでした。
/homeにアクセスするとエラーになるのでデフォルトのrouteも変更しておきます。
(function () {
'use strict';
angular.module('hoge', ['ngNewRouter'])
.controller('AppController', RootController);
function RootController() {
}
RootController.$routeConfig = [
{
path: '/',
redirectTo: '/home/sub1'
},
{
path: '/home',
component: 'home'
}
];
})();
おわりに
少しだけNew Routerをさわってみた感触としては、ハマりどころは若干ありそうですが、既にそれなりの完成度になっていると思います。
componentベースになっていたり、そのための標準的なディレクトリ構成が決まっていたりで、今のうちのこの構成に慣れておけば将来的にAngular2が安定した頃にはスムーズな移行ができるのではないでしょうか。
最後に宣伝ですが、2015年4月29日に開催されるGDG KobeのAngularJS勉強会でNew Routerを使ったハンズオンを担当させてもらいます。
興味を持たれた方は是非お越しください。