「AngularJS 入門」の続きです。

今回はルーティングと、フィルターです。

ルーティング

実は既にルーティングの設定はしてあるので見てみます。

// app/scripts/app.js
/* jshint indent: 4 */
'use strict';

angular
    .module('angularSampleApp', [
        'ngCookies', 'ngResource', 'ngSanitize', 'ngRoute'
    ])
    .config(function ($routeProvider) {
        // ルーティング設定
        $routeProvider
            .when('/', {
                templateUrl: 'views/main.html',
                controller: 'MainCtrl'
            })
            .otherwise({
                redirectTo: '/'
            });

    });

ルーティングはURLのマッチングパターンに対して、下記を設定するだけでOKです。

  • コントローラー名
  • ビューのパス

URLのマッチングパターンには パラメーター を受け取るようにする事ができるので、新しく定義してみます。

ルーティングを定義してみる

$routeProviderにルーティング定義を追加

// app/scripts/app.js
/* jshint indent: 4 */
'use strict';

angular.module('angularSampleApp', [
        'ngCookies', 'ngResource', 'ngSanitize', 'ngRoute'
    ])
    .config(function ($routeProvider) {
        $routeProvider
            .when('/', {
                templateUrl: 'views/main.html',
                controller: 'MainCtrl'
            })
            // この部分を追加
            .when('/frameworks/:name', {
                templateUrl: 'views/desc.html',
                controller: 'DescCtrl'
            })
            .otherwise({
                redirectTo: '/'
            });
    });

URLのマッチングパターンに:{変数名}を使うと、パラメータとして受け取る事ができます。

ルーティングに定義したDescCtrlを作る

// app/scripts/controllers/main.js
/* jshint indent: 4 */
'use strict';

angular.module('angularSampleApp')
    .controller('MainCtrl', function ($scope) {

        $scope.world = 'Angular';

        $scope.addFramework = function (text) {
            $scope.frameworks.push(text);
            $scope.world = '';
        };

    })
    // 新しく追加したコントローラー
    .controller('DescCtrl', function ($scope, $routeParams) {

        $scope.framework = {
            name: $routeParams.name
        };

    });

マッチしたルーティングのパラメータは、$routeParamsサービスから取得できます。

補足 引数の名前は$routeParamsでなければなりません。 位置はどこでもいいのですが、DIの回に説明するのでここでは割愛

ルーティングに定義したapp/views/desc.htmlを作る

<!-- app/views/desc.html -->
<pre>{{ framework }}</pre>

確認できればよいので変数のダンプ

準備ができたのでアクセスしてみます

ブラウザのURLを手動でhttp://127.0.0.1:9000/#/frameworks/hoge等にしてみるとイイ感じに表示されるはずです。

image

ルーティングが完成したので、次はリンクを貼ってみます。

リンクからのアクセス

先程手動でやった事をマークアップするだけでできてしまいます。

<!-- app/views/main.html -->
<p>
    <input type="text" ng-model="world">
    <span ng-click="addFramework(world)" class="btn btn-primary">Add</span>
</p>
<p>Hello {{ world }}!</p>

<p ng-init="frameworks = ['Backbone.js', 'Ember.js', 'Knockout.js']">
    <h3>{{ frameworks.length }} frameworks</h3>
    <ul>
        <!-- リンクに置き換えます -->
        <li ng-repeat="f in frameworks">
            <a ng-href="/#/frameworks/{{f}}">{{f}}</a>
        </li>
    </ul>
</p>

image

これでリンクが貼れます。

補足 本来はパラメーターをurlエンコードする必要がありますが、ここでは省略しています。

フィルター

データを変換する機構です。 ビュー上でフィルター専用のシンタックスがあるので、便利です。

フィルターを使ってみる

ビュー上で{{ 変数名|フィルター名 }}と記述すると、変数をフィルタリングした結果が表示されます。 もちろん元の変数は不変です。

単純なフィルター

<!-- app/views/main.html -->
<p>
    <input type="text" ng-model="world">
    <span ng-click="addFramework(world)" class="btn btn-primary">Add</span>
</p>
<p>Hello {{ world }}!</p>

<p ng-init="frameworks = ['Backbone.js', 'Ember.js', 'Knockout.js']">
    <h3>{{ frameworks.length }} frameworks</h3>
    <ul>
        <li ng-repeat="f in frameworks">
            <a ng-href="/#/frameworks/{{f}}">{{f|uppercase}}</a> <!-- 大文字にする -->
        </li>
    </ul>
</p>

image

uppercaseフィルターで大文字が表示されました。

オブジェクトに対するフィルター

フィルターが対応していれば、何に対しても使えます。

<!-- app/views/main.html -->
<p>
    <input type="text" ng-model="world">
    <span ng-click="addFramework(world)" class="btn btn-primary">Add</span>
</p>
<p>Hello {{ world }}!</p>

<p ng-init="frameworks = ['Backbone.js', 'Ember.js', 'Knockout.js']">
    <h3>{{ frameworks.length }} frameworks</h3>
    <ul>
        <li ng-repeat="f in frameworks | filter:'a'"> <!-- 文字'a'が含まれている要素のみ表示 -->
            <a ng-href="/#/frameworks/{{f}}">{{f|uppercase}}</a>
        </li>
    </ul>
</p>

image

'a'が含まれるBackbone.jsだけが表示されました。

ここではリテラルを書きましたが、フィルターに渡す値に変数を使う事ができます。

リアルタイムにフィルタリング

入力した値からリアルタイムにフィルタリングすることもできるのでやってみます。

<!-- app/views/main.html -->
<p>
    <input type="text" ng-model="world">
    <span ng-click="addFramework(world)" class="btn btn-primary">Add</span>
</p>
<p>Hello {{ world }}!</p>

<p ng-init="frameworks = ['Backbone.js', 'Ember.js', 'Knockout.js']">
    <h3>{{ frameworks.length }} frameworks</h3>
    <input type="text" ng-model="searchText" /> <!-- 検索窓を設置 -->
    <ul>
        <li ng-repeat="f in frameworks | filter:searchText"> <!-- 変数によるフィルタリング -->
            <a ng-href="/#/frameworks/{{f}}">{{f|uppercase}}</a>
        </li>
    </ul>
</p>

image

バインディングやテンプレートがなかったらかなり面倒ですが、Angularだとここまで簡単になります笑

フィルターの定義

フィルターの定義も簡単にできます。

// app/scripts/app.js
/* jshint indent: 4 */
'use strict';

angular.module('angularSampleApp', [
        'ngCookies', 'ngResource', 'ngSanitize', 'ngRoute'
    ])
    .config(function ($routeProvider) {
        $routeProvider
            .when('/', {
                templateUrl: 'views/main.html',
                controller: 'MainCtrl'
            })
            .when('/frameworks/:name', {
                templateUrl: 'views/desc.html',
                controller: 'DescCtrl'
            })
            .otherwise({
                redirectTo: '/'
            });
    })
    // フィルター `reverse` の定義
    .filter('reverse', function () {
        return function (values) {
            return values.concat().reverse();
        };
    });

.filter()にフィルター名と、 どう変換するかの処理を記述したクロージャーをreturnするクロージャー を渡せば良いです。

この例だと、ただ単に配列を逆順にするreverseフィルターです。

定義したフィルターを使ってみる

<!-- app/views/main.html -->
<p>
    <input type="text" ng-model="world">
    <span ng-click="addFramework(world)" class="btn btn-primary">Add</span>
</p>
<p>Hello {{ world }}!</p>

<p ng-init="frameworks = ['Backbone.js', 'Ember.js', 'Knockout.js']">
    <h3>{{ frameworks.length }} frameworks</h3>
    <input type="text" ng-model="searchText" />
    <ul>
        <li ng-repeat="f in frameworks | reverse | filter:searchText"> <!-- reverseフィルタを追加 -->
            <a ng-href="/#/frameworks/{{f}}">{{f|uppercase}}</a>
        </li>
    </ul>
</p>

image

逆順になりました! 使い方はビルドインのフィルタと同じですが、フィルターがネスト可能だと言う例も兼ねてます。

frameworks|reverse|reverseとすれば元に戻ります。

次回は、AngularJS入門(3) 〜サービス, DI〜の予定です。