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

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

【AngularJS】Directiveのscopeプロパティを理解する

最近AngularJSを触るようになって。一番最初に詰まったのがControllerとDirectiveの関係性でした。 今回はそこで必要になってくるDirectiveのscopeプロパティについてメモ。

scopeプロパティがfalseの時

HTML

<div ng-controller="MyCtrl">
  <div>Controller: {{ test }}</div>
  <my-directive></my-directive>
</div>

JavaScript

function myDirective(){
  return {
    scope: false,
    template: '<div>Directive: {{ test }}</div>',
    link: function(scope){
      scope.test = 'test!!!!';
    }
  }
}

angular.module('app').directive('myDirective', myDirective);

ディレクティブのscopeプロパティにfalseを指定すると、ディレクティブを使用している箇所のスコープを共有します。 なので、この場合はMyCtrlのスコープを共有することになります。

そのため、ディレクティブのscope.testはコントローラー側でも取得できるため、両方に'hoge'の文字が表示されます。

f:id:Im0_3:20150624194554p:plain

なお、scopeプロパティを省略した場合はfalseと同じ挙動になります。

scopeプロパティがtrueの時

function myDirective(){
  return {
    scope: true,
    template: '<div>Directive: {{ test }}</div>',
    link: function(scope){
      scope.test = 'hoge';
    }
  }
}

angular.module('app').directive('myDirective', myDirective);

次にscopeプロパティをtrueに変更します。 すると、今度はディレクティブを使用している箇所のscopeのインスタンスがディレクティブに渡ります。

そのため、この場合はコントローラー側の{{ test }}にはデータが渡らず、ディレクティブのみ'hoge'が表示されます。

f:id:Im0_3:20150624194639p:plain

このscopeは共有のスコープでもプロトタイプ継承をしたものでもなく新しいscopeとして存在しています。

もし親のscopeにアクセスしたい場合は$parentを使用します。

function myDirective(){
  return {
    scope: true,
    template: '<div>Directive: {{ test }}</div>',
    link: function(scope){
    }
  }
}

angular.module('app').directive('myDirective', myDirective);

このように、$parentを使用した場合は親のscopeのtestに'hoge'が追加されるので、結果は以下のようになります。

f:id:Im0_3:20150624194554p:plain

分離スコープを使う

スコープにはもうひとつ、分離スコープを使用する方法があります。
分離スコープはディレクティブの属性に値を指定すると、それがディレクティブのスコープとして登録されます。

スコープの渡し方は以下のように指定します。

  • = : データバインディングとして受け取る
  • @ : 文字列として受け取る
  • & : 関数として受け取る

以下がその例です。

HTML

<div ng-controller="MyCtrl">
  <input type="text" ng-model="name">
  <my-directive my-name="name" message="Hello" action="sayHello"></my-directive>
</div>

JavaScript

function myDirective(){
  return {
    scope: {
      myName: '=',
      message: '@',
      action: '&'
    },
    template: '<button ng-click="action()">Say {{message}} to {{myName}}.</button>'
  }
}

angular.module('app').directive('myDirective', myDirective);

まずディレクティブに属性を指定します。属性名が複数の単語になる場合はハイフンで指定します。

そして、ディレクティブのscopeプロパティで先ほど指定した属性の値の受け取り方を指定します。 先ほどの属性名をプロパティ名に指定します。プロパティ名はキャメルケースで記述します。

scope.myNameはデータバインディングとして渡しているため、フォームの中身を変更すると、自動的にディレクティブにも反映されます。

f:id:Im0_3:20150624194731p:plain

messageは文字列として受け取っているため、Helloという文字がそのまま渡されます。 actionにはsayHelloという関数が渡されています。ボタンをクリックした際に、その関数が実行されます。

f:id:Im0_3:20150624194745p:plain

分離スコープを利用すると、親のスコープの影響を受けにくくなります。コンポーネントとしていろいろな箇所で使う際には分離スコープを使用するほうがよさそうです。