AngularJSのTutorialのstep-9からカスタムFilterを利用する

AngularJSの[Tutorial]()の[step-8](http://docs.angularjs.org/tutorial/step_08)と[step-9](http://docs.angularjs.org/tutorial/step_09)についてレポートします。

step後半になると基本事項の説明はされているため([ディレクティブの説明](http://qiita.com/icoxfog417/items/59a07c9a74084114fe23)は無いようですが)、内容が濃くなってきてますが[step-8](http://docs.angularjs.org/tutorial/step_08)は薄いので取りまとめてちょこっと取り扱います。

[step-8](http://docs.angularjs.org/tutorial/step_08)のポイントは、URI内のパラメータの利用方法になると思います。
[step-9](http://docs.angularjs.org/tutorial/step_09)はFilterをCustomize指定利用する方法を解説していて、ディレクティブの次にカスタマイズして多分最も利用することが多くなる仕組みではないでしょうか。

##　[step-8](http://docs.angularjs.org/tutorial/step_08)

よくあるプロダクトの詳細ページを表示するデモページを元に課題を遂行します。
ここで、ポイントとして説明されているのは、REST由来のURIから表示するリソースのID情報を取得する部分になります。

### Controller

アクセス時に生成された$route サービスの$routeParamsを利用して、phoneIdに紐づくPhone情報のJsonファイルを取得している。

```js:app/js/controllers.js
phonecatControllers.controller('PhoneDetailCtrl', ['$scope', '$routeParams', '$http',
  function($scope, $routeParams, $http) {
    $http.get('phones/' + $routeParams.phoneId + '.json').success(function(data) {
      $scope.phone = data;
    });
  }]);
```

[step-7](http://docs.angularjs.org/tutorial/step_07)からControllerの引数がちゃんと指定されるようになってますね。$routeParamsを使うには、利用することをちゃんと宣言しなければならないようです。

ｊQueryではURL内のパラメータを取得することはそのままではできませんでした。
Angularでは、/phones/[phoneId].jsonとなっている場合、「[phoneId]」部分は$routeParamsを介してphoneId名で簡単に利用できるように準備してくれます。

> ## RESTなURLの補足
> Qiitaの読者の殆どは玄人が多いので、基本的なことで触れる必要もないかもしれませんが、むりくりURL解析とか、JavascriptのqueryString解析とかそんなことはする必要ありません。
> ここでphoneIdがなぜ取得できるかというと、$routeProviderの設定のwhen引数で「/phones/:phoneId」と設定したためで、「:phoneId」の部分はURIの中の値が動的であり、プログラムで利用したい事を指定しているわけです。
> 
> たとえば、下記のようなURL、
> 
> ```
> /communities/3/topic/357
> ```
> この場合、$routeProviderのwhen引数では、
> 
> ```
> /communities/:communityId/topic/:topicId
> ```
> となります。ID部分の文字列はなんでもいいと思いますが、通常はリソース名+Idとなるのが通例ですね。
> RESTでURLを設計すると上記の形になります。
> 昔ながらの/topic?id=357&communityId=3という形で組んでしまうと、一見URLだけを見ただけだと「/topic」となり何を引数にしているのかわかりません。
> 上記のようにURL内にIDを明示できる設計にすることで、よりプログラマブルで開発時やテスト時にも優しい設計になります。ここら辺の話は、クライアントフレームワークの基本的な事項みたいですね。

テストに関しては特に新しい事項には触れられていないため、省略です。

## [step-9](http://docs.angularjs.org/tutorial/step_09)

AngularのFilterをカスタマイズする方法を説明してくれているstepですね。
大分説明がはしょられていますが、Filter部分は下記のコードになっています。

```js:app/js/filters.js
'use strict';

/* Filters */

angular.module('phonecatFilters', []).filter('checkmark', function() {
  return function(input) {
    return input ? '\u2713' : '\u2718';
  };
});
```
ここでも、[module](http://docs.angularjs.org/guide/module)ですね。
phonecatFiltersという名前のモジュールを定義して、checkmarkという名前のカスタムFilterを定義しています。
inputは、テンプレート上でいうところのフィルターの「|」の手前変数に格納されている値をさします。
デモでは、スマホのオプション機能があるかないかのbool値が渡されてきて、true|falseに合わせてアイコンを返しています。

サーバ側のテンプレートエンジンでも重宝するこのカスタムFilter機能を、クライアント側でも同様に利用できることが幸せです。
たとえばユーザのSignatureを出すとき、jsonのデータを格納しているuserというModel変数があったとしたら、

```html
{{ user | signature }}
```
として、出力は

```
<div class="signature">
  <span class="name">今西 徹</span>
  <span class="age">（23歳)</span>
  <span class="simple-address"> 大阪在住</span>
</div>
```
とか、テンプレート側のシンプルな記述とは異なり、userに格納されている情報を自由に活用して、HTMLを出せるようになります。
様々なページで共通化しておけば、表示方法の修正はFilterだけを直せば良くなります。

### テスト

当然ながらこういうパーツのテストも書けますし、書くべきです。
テストを積み重ねることによって、安定なシステム構築につながります。

```js:test/unit/filtersSpec.js
describe('filter', function() {
 
  beforeEach(module('phonecatFilters'));
 
  describe('checkmark', function() {
 
    it('should convert boolean values to unicode checkmark or cross',
        inject(function(checkmarkFilter) {
      expect(checkmarkFilter(true)).toBe('\u2713');
      expect(checkmarkFilter(false)).toBe('\u2718');
    }));
  });
});
```

1. beforeEachにて定義されたFilterを読み込む
1. itブロックでcheckmarkFilterを利用できるように、[angular.mock.inject](http://docs.angularjs.org/api/angular.mock.inject)を利用して参照を作成します
1. true/falseで正しい値が返ってきていることをexpectで調べています

以上、カスタムFilterのstepでした。