New Routerの重箱の隅をつつく


# はじめに

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はいずれ違う名前になるんじゃないかと思われます）。

```javascript:app.js
(function () {
  'use strict';

  angular.module('hoge', ['ngNewRouter'])
    .controller('AppController', RootController);

  function RootController() {
  }

  RootController.$routeConfig = [
    {
      path: '/',
      redirectTo: '/home'
    },
    {
      path: '/home',
      component: 'home'
    }
  ];
})();
```

```html:index.html
<!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で解消予定だそうです。

* https://github.com/angular/router/issues/187

ドキュメントやサンプルを見ると、`$routeConfig`でルート設定を行った上、controllerに`$router`をDIしている例が見られますが、`$router`をDIする必要はとくになさそうです。

## $router.config

今度は$routerを使ったroute設定です。

```javascript:app.js
(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は以下のように記述します。

```javascript:components/home/home.js
(function () {
  'use strict';

  angular.module('hoge')
    .controller('HomeController', HomeController);

  function HomeController() {
    this.header = 'ハロー！';
  }
})();
```

```html:components/home/home.html
<h1>{{home.header}}</h1>
```

サンプルでは、componentを`angular.module('hoge.home', [])`のように別モジュールに分けていましたが、分けなくても特に問題はないようです。

さらに、index.htmlにscriptタグを追加します。

```html:index.html
<!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を以下のように変更します。

```javascript:components/home/home.js
(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'
    }
  ];
})();
```

```html:components/home/home.html
<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の定義です。


```html:index.html
<!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>
```

```javascript:components/sub1/sub1.js
(function() {
  angular.module('hoge')
    .controller('Sub1Controller', Sub1Controller);

  function Sub1Controller() {
  }
})();
```

```html:components/sub1/sub1.html
<h2>sub1</h2>
```

```javascript:components/sub2/sub2.js
(function() {
  angular.module('hoge')
    .controller('Sub2Controller', Sub2Controller);

  function Sub2Controller() {
  }
})();
```

```html:components/sub2/sub2.html
<h2>sub2</h2>
```

子componentもcomponentsディレクトリにフラットに配置すればよさそうでした。

/homeにアクセスするとエラーになるのでデフォルトのrouteも変更しておきます。

```javascript:app.js
(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勉強会](http://www.zusaar.com/event/6007003)でNew Routerを使ったハンズオンを担当させてもらいます。
興味を持たれた方は是非お越しください。