前回の続きです。

今回はDirective(ディレクティブ)についてです。

ngModelngRepeatなどを既に使っているのでディレクティブがどんなものかは大体分かっているかと思います。

ビルドインのディレクティブ一覧 => http://docs.angularjs.org/api/ng#directive

####なので早速ですがディレクティブを作ります。

単純なディレクティブ

レイアウトを持たず、動作を追加するディレクティブを作ってみます。

ちょうどTwitter Bootstrapが雛形に使われているので、リンクにツールチップを追加します。

<!-- 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>

<div>
    <h3>{{ frameworks.length }} frameworks</h3>
    <input type="text" ng-model="searchText" />
    <span ng-hide="frameworks">読み込み中...</span>
    <ul>
        <li ng-repeat="f in frameworks | reverse | filter:searchText">
            <!—- tooltip属性を追加する ->
            <a ng-href="/#/frameworks/{{f}}" tooltip>{{f|uppercase}}</a>
        </li>
    </ul>
</div>

tooltipディレクティブを定義します。

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

angular.module('angularSampleApp', [
        'ngCookies', 'ngResource', 'ngSanitize', 'ngRoute'
    ])
    .factory(‘…’, function () {})
    // tooltip ディレクティブを定義
    .directive('tooltip', function () {
        return function (scope, element, attr) {

            element.tooltip({
                title: 'tooltip test'
            });

        };
    });

省略型はクロージャを返却して定義とします。 この場合はtooltipディレクティブが設定されているDOMに対して、ツールチップを表示するだけです。

scope element attr は インジェクターからインジェクトされた物ではないです。

image

elementはただのjQueryオブジェクトなので、DOM操作はなんでもできますね!

ディレクティブにパラメータを渡す

先ほど作ったディレクティブを少し弄ります。

<!-- 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>

<div>
    <h3>{{ frameworks.length }} frameworks</h3>
    <input type="text" ng-model="searchText" />
    <span ng-hide="frameworks">読み込み中...</span>
    <ul>
        <li ng-repeat="f in frameworks | reverse | filter:searchText">
            <!—- ディレクティブに変数を渡す ->
            <a ng-href="/#/frameworks/{{f}}" tooltip="f">{{f|uppercase}}</a>
        </li>
    </ul>
</div>

tooltipディレクティブに変数fを渡す。

// app/scripts/app.js

    .directive('tooltip', function ($parse) { // expressionを評価するためのサービス
        // オプションを指定する場合はオブジェクトを返す
        return {
            restrict: A,
            link: function (scope, element, attr) {

                // attr.tooltip=“f” を指定したので、fの中身を取得し、ツールチップに設定
                // scope.$eval(attr.tooltip) でもよい
                var value = $parse(attr.tooltip)(scope);

                element.tooltip({
                    title: value
                });

            }
        };
    });

attr.tooltipの中身はただの文字なので、評価した結果をツールチップに渡します。

image

値の変更を監視してjsを実行したい場合は、scope.$watch() を使えばよいです。 http://docs.angularjs.org/api/ng.$rootScope.Scope#methods_$watch

restrict?

ディレクティブのrestrictはディレクティブの対象を制限する為に使います。

  • E <some-directive></some-directive>
  • A <div some-directive=“”></div>
  • C <div class=“some-directive”></div>
  • M <!-— directive: some-directive -—>

複数指定する場合は restrict: ‘AEC’ の様に繋げて書きます。

テンプレート

ディレクティブにはテンプレートの機能があります。

// app/scripts/app.js

    .directive('tooltip', function ($parse) {
        return {
            restrict: 'A',
            // テンプレートを追加
            template: '<span class="glyphicon glyphicon-asterisk"></span>',
            link: function (scope, element, attr) {

                var value = $parse(attr.tooltip)(scope);

                element.tooltip({
                    title: value
                });

            }
        };
    });

image

一応これだけでテンプレートが反映されますが、固定文ではダメなのでngTranscludeを使います。

ngTransclude

// app/scripts/app.js

    .directive('tooltip', function ($parse) {
        return {
            restrict: 'A',
            // テンプレートを編集
            template: '<span class="glyphicon glyphicon-asterisk"></span> <i ng-transclude></i>',
            // transcludeを設定
            transclude: true,
            link: function (scope, element, attr) {

                var value = $parse(attr.tooltip)(scope);

                element.tooltip({
                    title: value
                });

            }
        };
    });

image

テンプレートのどこに内容をセットするかをngTranscludeで指定するだけなので簡単です。

ディレクティブ間の連携

公式ドキュメント(http://docs.angularjs.org/guide/directive#creating-custom-directives_demo_creating-directives-that-communicate)にいい感じの説明があるので、今回は省略します。