ハッカーを目指す白Tのブログ

夏は白のTシャツを着ています。ラジオが好きです。(ラジオネーム: 隠れキリジダン)

AngularJSのディレクティブについて

明日AngularJSのハッカソン(AngularJS ハッカソン - AngularJS Japan User Group | Doorkeeper)に参加することになったので、AngularJSを一から勉強してみることにした。そこで今回は、AngularJSのディレクティブについて解説していきたい。

1. AngularJSのディレクティブとは

AngularJSのディレクティブとは、双方向バインドを実現するための仕組みである。

AngularJSで言うところの双方向バインドでは、具体的に以下の2つの処理が行われる。
1.Model(=scope)の変更をView(=DOM)へ反映する
2.Viewの変更時にscopeの値を変更する

具体的に、双方向バインドは、AngularJSのHTMLコンパイラが、DOM要素(クラス、属性、HTMLタグ、コメント等)からディレクティブを検出し、そこに対応するスクリプトを埋め込むことで実現される。

AngularJSには標準で組み込まれているディレクティブが多数ある。
例えば、テキストボックスの入力値とscope.nameを紐付ける場合、htmlに下記を記述するだけで、ユーザの入力値がscopeへ即時反映される。

<input type="text" ng-model="name" />
<span>{{name}}</span>

他にも、ngBind、 ngRepeat や ngClassなどのディレクティブがある。詳しくは、こちらの標準ディレクティブ一覧を参照して頂きたい。(https://docs.angularjs.org/api/ng/directive)

2. カスタムディレクティブ

実装したい機能が標準のディレクティブを使って出来ない場合は、自分で自由にディレクティブを作る事ができる。
これをカスタムディレクティブと言う。以下、作る際の注意点を二つ挙げる。

1.ディレクティブの定義には、angular.moduleのdirective関数を利用すること
2.ディレクティブをスクリプトファイルで定義する際はキャメルケースを用いるが、テンプレートで利用する時はハイフン繋ぎ(アンダースコア、コロンでも良い)にすること

以下、簡単なカスタムディレクティブを作ってみた。例としてあまり適切でないかもしれないが、下記を実行すれば、「初めてのディレクティブ」が表示されたはずだ。

//myModule.js
var myModlue = angular.module('myModlue', []);
myModule.directive('firstDirective', function(){
  return {
    template: '<span>初めてのディレクティブ</span>'
  }; 
});
<!doctype html>                                                 
<html ng-app="myModule">                                        
<head>                                                          
<script type="text/javascript" src="angular.min.js"></script>   
<script type="text/javascript" src="myModule.js"></script>      
</head>                                                         
<body>                                   
<div first-directive></div>                                     
</body>                                                         
</html>                                                         

3. ディレクティブのオプションでできることまとめ

カスタムディレクティブには、様々なオプションが用意されている。以下、それぞれのオプションでできることを一覧で示す。
なお、各オプションの詳しい解説は$compile | AngularJS 1.2 日本語リファレンス | js STUDIOを参照していただきたい。


1. ディレクティブ同士の優先順位を指定する
2. 新しいscopeをディレクティブ内で指定する
3. ひとつのディレクティブで複数のDOM要素を指定する
4. 指定するディレクティブよりpriorityの低いディレクティブの適用を外す
5. ディレクティブのscopeをcontrollerで使用可能にする
6. コントローラのコンストラクタ関数を定義する
7. 他のディレクティブで生成されたcontrollerを使う
8. ディレクティブがもつtemplate内で使えるディレクティブのcontrollerのエイリスを指定する
9. templateでのディレクティブの指定方法を定義する
10. ディレクティブがもつtemplateをhtml文字列として指定する
11. ディレクティブがもつtemplateをhtmlファイルのパスで指定する
12. templateでディレクティブを指定する際、子要素を挿入可能にする
13. scopeとtemplateを結びつける処理を指定する(compile)
14. scopeとtemplateを結びつける処理を指定する(link)

1. ディレクティブ同士の優先順位を指定する

priority: 10 //数字が大きければ大きいほど、優先度が高く先にコンパイルされる。

2. 新しいscopeをディレクティブ内で指定する

scope: true //true にすることで、新しいscopeが作成される

scope: {                              //hashオブジェクトを渡すことで、親scopeを継承しない'隔離scope'が作成される
      myDirective: '=',          // '='は、双方向バインディング
      onChange: '&',            // '&'は、指定した値を親scopeのcontextで実行する
      title: '@'                      // '@'は、ローカル(このディレクティブの)scopeの値をDOMに追加する  
    }       

3. ひとつのディレクティブで複数のDOM要素を指定する

multiElement : true   

4. 指定するディレクティブよりpriorityの低いディレクティブの適用を外す

terminal : true //priorityとともに使う。trueに設定した場合、そのディレクティブよりpriorityが小さいディレクティブは実行されない。

5. ディレクティブのscopeをcontrollerで使用可能にする

bindToController : true 

”隔離scope"がディレクティブで作成されて、controllerAsが使われているとき、bindToController : trueにすると、その作成された"隔離scope"が、controllerAsで指定されたcontrollerがインスタンス化された時に使用可能になる。

6. コントローラのコンストラクタ関数を定義する

controller : function($scope) {
        var panes = $scope.panes = [];
        $scope.select = function(pane) {
          angular.forEach(panes, function(pane) {
            pane.selected = false;
          });
          pane.selected = true;
        };

7. 他のディレクティブで生成されたcontrollerを使う

require : 'sample-directive' //ディレクティブ名を指定する

8. ディレクティブがもつtemplate内で使えるディレクティブのcontrollerのaliasを指定する

controllerAs : 'sample-controller'

9. templateでのディレクティブの指定方法を定義する

restrict :  'E'     //E:タグ  ex)<sample-directive>

//他にも、
// A : 属性      ex)   <div sample-directive>
// C : クラス      ex)  <div class="sample-directive;">
// M : コメント  ex)  <!-- directive: sample-directive -->

10. ディレクティブがもつtemplateをhtml文字列として指定する

template : '<div red-on-hover>{{delete_str}}</div>'

11. ディレクティブがもつtemplateをhtmlファイルのパスで指定する

templateUrl : 'directive.html'         //loadは非同期で行われる

12. templateでディレクティブを指定する際、子要素を挿入可能にする

transclude : true

13. scopeとtemplateを結びつける処理を指定する(compile)

compileと14のlinkオプションは同時に使うことが出来ない。詳しい使い方については、JavaScript - AngularJSのDirectiveを理解する. - Qiitaを参照頂きたい。

compile : function compile(tElement, tAttrs, transclude) {
      return {
        pre: function preLink(scope, iElement, iAttrs, controller) { ... },
        post: function postLink(scope, iElement, iAttrs, controller) { ... }
      }
    }

14. scopeとtemplateを結びつける処理を指定する(link)

link : function link(scope, element, attrs) {
    var format,
        timeoutId;

    function updateTime() {
      element.text(dateFilter(new Date(), format));
    }

    scope.$watch(attrs.myCurrentTime, function(value) {
      format = value;
      updateTime();
    });

    element.on('$destroy', function() {
      $interval.cancel(timeoutId);
    });

    // start the UI update process; save the timeoutId for canceling
    timeoutId = $interval(function() {
      updateTime(); // update DOM
    }, 1000);
  }

参照URL


JavaScript - AngularJSのDirectiveを理解する. - Qiita
https://docs.angularjs.org/api/ng/service/$compile
https://docs.angularjs.org/guide/directive
JavaScript - AngularJSのdirectiveとは - Qiita

以上