子 scope を生成する場合
公式のドキュメントを読めば大体書いてある。
https://docs.angularjs.org/guide/directive
https://docs.angularjs.org/api/ng/service/$compile#directive-definition-object
directive 作成時のオブジェクトに scope プロパティを指定するとあらたに子 scope が作られる。そのときの値によって、親 scope 内のプロパティとどのように対応するかを指定できる。
指定値 | 意味 |
---|---|
@ | 属性値を値としてマッピング |
= | 親 scope のプロパティとマッピング |
& | 属性値を評価式としてマッピング |
実際のコードで定義してみる。
app.directive('counter', function () {
return {
restrict: 'E',
scope: {
onCountup: '&',
current: '=',
prefix: '@'
},
template: '<div>click here</div>',
link: function ($scope, element, attrs) {
var n = 0;
element.on('click', function () {
$scope.onCountup();
$scope.current = $scope.prefix + (++n).toString();
$scope.$apply();
});
}
};
});
こういうものがあるとき、次のように使える。
<div>{{countup}}</div>
<div>{{countdown}}</div>
<counter prefix="now:" current="countup" on-countup="countdown = countdown - 1" ng-init="countup = 0; countdown = 10">
次の 3 点がポイント。
- prefix 属性に指定した値がそのまま directive 内の scope の prefix プロパティに入っている
- current 属性に指定したプロパティ名と directive 内の scope の current プロパティと同期する
- directive から親の scope に値を出力できる。
- on-countup 属性に指定した式を directive 内の scope の onCountup プロパティで実行できる
- directive に追加の挙動をもたせたり、動作する条件を指定させたりできる。
というあたりを使えば難しくない。
子 scope を生成しない場合
restrict に "A" を含む場合、つまり属性として作用できる directive の場合、 scope を指定してしまうと、同様に scope を指定した directive を同時に適用できなくなってしまう。次はダメな例。
<counter directive-with-scope>
同一要素に対して scope を 2 つ生成しようとしてるんだからまぁどうやって制御するんだって話しになる。
この場合、子 scope が作られないわけなので直接いじってしまえばいい。ただし、 directive が scope を変に汚染したり、既存のプロパティを上書きしないように気をつける。
app.directive('sample', function () {
return {
restrict: 'A',
link: function ($scope, element, attrs) {
var propertyName = attrs.sample;
$scope[propertyName] = 'sample attr';
$scope.$eval(attrs.expression);
}
};
});
<div>{{foo}}</div>
<div>{{sample}}</div>
<div sample="sample" expression="foo = true">
link に指定する関数の引数の 3 つ目 attrs に directive と一緒に指定されている属性すべてが入っているので、これを利用する。
- ある属性値がほしければ attrs のプロパティを取得する
- directive を使う側から指定されたプロパティ名を使って scope をいじるとうっかり上書きすることもない
- 式の評価には scope 内の $eval を使う
これで scope 指定と同等のことができる。