フロントエンドとコーヒー

フロントエンドとか日々のこととか

【AngularJS】 Directiveのreqireプロパティと親子の関係

今日は Directiveのrequireプロパティと親子関係について。

ディレクティブは入れ子にした場合、子のディレクティブのreqireプロパティから親のティレクティブのコントローラーを呼ぶことができます。

まずはデモのソースから。

DEMO

HTML

<my-directive parent="hoge">
  <child-directive child="1"></child-directive>
  <child-directive child="4"></child-directive>
  <child-directive child="8"></child-directive>
</my-directive>

JavaScript

//コントローラー関数
function myCtrl(){
  this.list = [];
  this.add = function(item){
    this.list.push(item);
  }
  this.sum = function(){
    var value = 0;
    for (var i = 0; i < this.list.length; i++) {
      value += this.list[i];
    }
    return value;
  }
}

//親のディレクティブ
function myDirective(){
  return {
    controller: myCtrl,
    scope: {
      parent: '@',
    },
    link: function(scope, element, attrs, ctrl){
      element.html('parent is ' + scope.parent + '<br>');
      element.append('Total child value is' + ctrl.sum());
    }
  }
}
angular.module('app').directive('myDirective', myDirective);

//子のディレクティブ
function childDirective(){
  return {
    require: '^myDirective',
    scope: {
      child: '@',
    },
    link: function(scope, element, attrs, ctrl){
      ctrl.add(parseInt(scope.child));
    }
  }
}
angular.module('app').directive('childDirective', childDirective);

解説

myDirectiveの中にchildDirectiveが含まれた構造になっています。
myDirectiveにはparent、childDirectiveにはchildという属性をつけて、それぞれのディレクティブのscopeプロパティで文字列としてscopeに追加しています。

そしてこのディレクティブ全体の制御するために、myCtrlというコントローラーを用意しました。

コントローラーで主な処理を行い、最終的にmyDirectiveでparent属性と、child属性の合計の数を出力します。

f:id:Im0_3:20150625205950p:plain

ここで用意したコントローラーはmyDirectiveのcontrollerプロパティで指定します。

function myDirective(){
  return {
    controller: myCtrl,
    //中略
    link: function(scope, element, attrs, ctrl){
      //中略
      element.append('Total child value is' + ctrl.sum());
    }
  }
}

controllerプロパティで指定するとlink関数の第4引数でコントローラーを引っ張ってくることができます。 ここではsum()という関数を引っ張ってきています。

次に子のディレクティブです。

function childDirective(){
  return {
    require: '^myDirective',
    scope: {
      child: '@',
    },
    link: function(scope, element, attrs, ctrl){
      ctrl.add(parseInt(scope.child));
    }
  }
}

子のディレクティブのlink関数の第4引数はどのコントローラーを指すのか。実は親のディレクティブが持つコントローラーを指しています。

それを指定することができるのがrequireプロパティです。

require

requireには依存するディレクティブを指定します。指定すると、そのディレクティブのコントローラーをlink関数の第4引数として受け取ることができます。

requireプロパティには以下の様なオプションが存在します。

  • ? : requireで指定したディレクティブを省略可能にする
  • ^ : 依存するディレクティブを先祖の要素から探す

今回は先祖の要素から探すため^を付けて、親のディレクティブ名を指定しました。 このため、myCtrladd()が使用することが出来ました。

もし何も付けないで使用する場合はディレクティブにng-model属性をつけて使うパターンが多いようです。

HTML

<another-directive ng-model="item"></another-directive>

JavaScript

function anotherDirective(){
  return {
    require: 'ngModel',
    scope: {
      child: '@',
    },
    link: function(scope, element, attrs, ctrl){
      //何らかの処理
    }
  }
}

この場合はng-modelに指定したティレクティブに依存するようになります。

上記のコードで、ng-model属性をつけ忘るとエラーが発生します。ただしrequreプロパティの先頭に?をつけると、省略が可能になるのでエラーが発生しなくなります。

まとめ

今回のようにディレクティブが入れ子になる際はrequireプロパティを使用すると、同一のコントローラーを使用できるようになります。 複数のディレクティブで構成されたコンポーネントを作る際は重要になりそうなので、ちゃんと使えるようになりたいですね。

AngularJSはまだまだ奥が深いけど、関係を少しずつ理解してくると楽しい。