--- title: Angular.js入門 (5)ディレクティブ その2 tags: JavaScript Angular author: _rdtr slide: false --- 前回: [Angular.js入門 (4)ディレクティブ その1](http://qiita.com/_rdtr/items/5edffeb96a9663fa1b1c) ## DirectiveとControllerの連携 ディレクティブとコントローラとで値を共有したいときはどうすれば良いのでしょう? 結論から言うとスコープを通じて渡してやれば良いです。 具体的には ```js:main.js var app = angular.module("myApp", []); app.controller("AppCtrl", function ($scope) { $scope.alertTest = function() { alert("テストだよ!"); }; }); ``` こんなコントローラの, alertTest関数をdirective内で使いたいとしましょう。コントローラAppCtrlのスコープ配下ではalertTestという関数が生きていることになるのでhtml側で以下のように記述します。 ```index.html Angular.jsのテスト
この上にマウスを乗せてみる。
``` enter属性の値として, 関数名()を渡していることに注目してください。 あとは ```js:main.js var app = angular.module("myApp", []); app.controller("AppCtrl", function ($scope) { $scope.alertTest = function() { alert("テストだよ!"); }; }); app.directive("enter", function () { return function (scope, element, attrs) { element.bind("mouseenter", function () { scope.$apply(attrs.enter); }); }; }); ``` と, enterディレクティブを追加します。$applyというメソッドを用いてattrs.enter = "alertTest()"関数を適用していることになります。()をつけているので関数は即実行され, ![alertTest()の実行](https://qiita-image-store.s3.amazonaws.com/0/20470/47caa0c6-e24d-7c53-bbdf-27a2754cae11.png) テキストの上にマウスを乗せるとアラートが上がります。 ちなみにこのapplyメソッド, Angular自体が裏でバリバリに使いまくっているものなのですが, ユーザが自分で使うのはAngular由来ではない関数を最初のページ読み込み時以降に呼び出すケースだけというのは覚えておいてもいいかもしれないです。 ## Directive間の連携 今度はディレクティブ同士の間での連携です。どうやってやるのかというと, 連携したい先のディレクティブで連携元のディレクティブをrequireします。具体例です。 ```js:main.js var app = angular.module("myApp", []); app.directive("test1", function () { return { controller: function ($scope) { $scope.test_array = []; this.addHoge = function () { $scope.test_array.push("hoge"); }; this.addFuga = function () { $scope.test_array.push("fuga"); }; }, link: function (scope, element) { element.bind("mouseenter", function(){ console.log(scope.test_array); }); } }; }); ``` まずは上のようなtest1ディレクティブを用意します。ここでダミー配列と, 配列に要素を追加する関数を2つ(addHoge, addFuga)用意しています。これだけだとまだ何も起きないので, これらの関数を使うもう1個ディレクティブを追加しましょう。 ```js:main.js var app = angular.module("myApp", []); app.directive("test1", function () { return { controller: function ($scope) { $scope.test_array = []; this.addHoge = function () { $scope.test_array.push("hoge"); }; this.addFuga = function () { $scope.test_array.push("fuga"); }; }, link: function (scope, element) { element.bind("mouseenter", function(){ console.log(scope.test_array); }); } }; }); app.directive("test2", function () { return { require: "test1", link: function (scope, element, attrs, test1Ctrl) { test1Ctrl.addHoge(); } }; }) ``` test2ディレクティブでtest1ディレクティブをrequireしています。そのあとlink関数にtest1Ctrlという引数が追加されています。ここにtest1のcontrollerがパラメータとして引き渡されるというわけです。 ![ディレクティブ同士の連携](https://qiita-image-store.s3.amazonaws.com/0/20470/1cb9fa62-6910-c885-cab1-752854a983c4.png) マウスを乗せるとhogeが追加されたtest_arrayがConsoleに出力されます。 もう少し見てみましょう。htmlを以下のように変えます。 ```html:index.html Angular.jsのテスト
この上にマウスを乗せてみる。
この上にマウスを乗せてみる。
``` FoundationのボタンClassの装飾があたるので ![ボタン化](https://qiita-image-store.s3.amazonaws.com/0/20470/cd27bf8b-d3d6-c23a-f9c0-1ab2e916b497.png) こんな見た目になるかと思います。 ここでjsを ```js:main.js var app = angular.module("myApp", []); app.directive("test1", function () { return { controller: function ($scope) { $scope.test_array = []; this.addHoge = function () { $scope.test_array.push("hoge"); }; this.addFuga = function () { $scope.test_array.push("fuga"); }; }, link: function (scope, element) { element.bind("mouseenter", function(){ console.log(scope.test_array); }); } }; }); app.directive("test2", function () { return { require: "test1", link: function (scope, element, attrs, test1Ctrl) { test1Ctrl.addHoge(); } }; }) app.directive("test3", function () { return { require: "test1", link: function (scope, element, attrs, test1Ctrl) { test1Ctrl.addFuga(); } }; }) ``` とします。新しいtest3ディレクティブを作り, こちらにmouseenterしたら"fuga"を出力したいわけですね。 実際にやってみましょう。 ![fuga!!](https://qiita-image-store.s3.amazonaws.com/0/20470/6209c8c8-66de-9af1-fcd1-8d5dacd925c7.png) 左のボタンにマウスを乗せたらhogeが出てほしいのに, 両方ともfugaになってしまいました。これはAngularのscopeが, デフォルトで「同じ要素で複数のディレクティブが新しいスコープをつくろうとすると, 1つしか作らない」という動きになっているためです。 javascriptのprototypeの話を思い出してください。 ```javascript function Dog() {} Dog.prototype.bark = function() { console.log('わんわん'); }; var dog = new Dog(); dog.bark(); //'わんわん'; ``` dogオブジェクトそのものにはbarkメソッドはありませんが, Dogを継承しているのでそのプロトタイプを遡ってbarkメソッドを参照し, 実行するのでした。逆にdogでだけbark関数の動きを変えたければ ```javascript dog.bark = function () {console.log("ばうばう");} dog.bark(); //'ばうばう'; ``` としてやればdogオブジェクト内にbarkがあるので遡って探しにいかないわけです。 ```javascript dog.bark = function () {console.log("ばうばう");} dog.bark = function () {console.log("きゃんきゃん");} dog.bark(); ``` ではこれはどうなるでしょう?同じbarkを上掛しているので「きゃんきゃん」に決まっていますね。ここでいう「同じbark」というのは, 同じスコープ内にあるbarkという変数ということです。 Angularのスコープも, javascriptのオブジェクトに変わりはありません。コントローラを作ると新しいスコープがそのコントローラ内に作られます。ディレクティブについても同様です。今、test2により新しいスコープが作成されたのですが, test3がそれを上書いてしまったので, test2のディレクティブでもtest3と同じ動きになってしまったのです。 ## Isolated scope まだイメージレベルでしか理解できないと思いますが, ここではとりあえずどうすれば良いのかを先に書いておきます。scopeはオブジェクトで一般のプロトタイプ継承に従うと言いましたが, Angularではこのルールを破ってプロトタイプのリンクをぶったぎり, 独自のスコープ(Isolated scope)を作ることができます。 ```js:main.js app.directive("test1", function () { return { scope: {}, controller: function ($scope) { $scope.test_array = []; this.addHoge = function () { $scope.test_array.push("hoge"); }; this.addFuga = function () { $scope.test_array.push("fuga"); }; }, link: function (scope, element) { element.bind("mouseenter", function(){ console.log(scope.test_array); }); } }; }); app.directive("test2", function () { return { require: "test1", link: function (scope, element, attrs, test1Ctrl) { test1Ctrl.addHoge(); } }; }) app.directive("test3", function () { return { require: "test1", link: function (scope, element, attrs, test1Ctrl) { test1Ctrl.addFuga(); } }; }) ``` たった1行。`scope: {}`と追記することで, 子スコープはisolatedになることを指定できるのです。 ![hoge&fuga](https://qiita-image-store.s3.amazonaws.com/0/20470/e8fc2649-9594-3ee8-5e3e-7a7307477f7c.png) きちんとお互いの関数が出力されるようになりました。 次回以降はスコープについてもうちょっと詳しく見て行きたいと思います。