AngularJS イベント通知 Scope#$emit, Scope#$broadcast, Scope#$on
$scopeのイベント通知関連の関数は以下の3つがある
- Scope#$emit 派生元に向けてイベントを送信(自身を含む)
- Scope#$broadcast 派生先に向けてイベントを送信(自身を含む)
- Scope#$on イベントを受信
リファレンス
https://code.angularjs.org/1.4.7/docs/api/ng/type/$rootScope.Scope#$on
$emitと$on
$emit
$emitは自身と派生元に向けてイベントを投げます
イベントを区別するための名前と渡したいデータを指定
$emit(name, args);
$on
$onはイベントを受け取ります
$onはイベント名とイベントを受け取るコールバックを指定
$on(name, listener);
コールバックはeventとデータを受け取る事が可能
function(event, ...args)
eventの中には以下の4つのプロパティが存在する(メソッドは後述)
- targetScope // $emitを実行した時のScope
- currentScope // $onが呼ばれた時のScope
- name // イベント名
- defaultPrevented // イベントを防ぐ?動作変更用らしい
親、子、孫で同一イベント名を定義して孫から$emitする
angular.module('myapp', []) .value('printLog', function(event, data) { console.log('event.name:' + event.name) // イベント名 console.log('event.targetScope.text:' + event.targetScope.text); // $emitないし$broadcastした$scopeからtextを取り出す console.log('event.currentScope.text:' + event.currentScope.text); // myControllerの$scopeからtextを取り出す console.log('event.defaultPrevented:' + event.defaultPrevented); // true or false (デフォルトはfalse) console.log('data:' + data); }) .controller('myController', function ($scope, printLog) { $scope.$on('MyEvent', function (event, data) { console.info('myController-MyEvent'); printLog(event, data) }); $scope.text = "myControllerのscope"; }) .controller('myControllerChild', function ($scope, printLog) { $scope.$on('MyEvent', function (event, data) { console.info('myControllerChild-MyEvent'); printLog(event, data); }); $scope.text = "myControllerChildのscope"; }) .controller('myControllerGrandChild', function ($scope, printLog) { $scope.$on('MyEvent', function (event, data) { console.info('myControllerGrandChild-MyEvent'); printLog(event, data); }); $scope.text = "myControllerChildGrandchildのscope"; $scope.$emit('MyEvent', 'myControllerGrandChild-$emit-MyEvent'); });
// 実行結果 myControllerGrandChild-MyEvent event.name:MyEvent event.targetScope.text:myControllerChildGrandchildのscope event.currentScope.text:myControllerChildGrandchildのscope event.defaultPrevented:false data:myControllerGrandChild-$emit-MyEvent myControllerChild-MyEvent event.name:MyEvent event.targetScope.text:myControllerChildGrandchildのscope event.currentScope.text:myControllerChildのscope event.defaultPrevented:false data:myControllerGrandChild-$emit-MyEvent myController-MyEvent event.name:MyEvent event.targetScope.text:myControllerChildGrandchildのscope event.currentScope.text:myControllerのscope event.defaultPrevented:false data:myControllerGrandChild-$emit-MyEvent
name、targetScope、dataはすべて同じ
currentScopeは必ず変わりdefaultPreventedは後述するpreventDefault()
を使用することで変化します
$emitの伝搬図
$broadcast
$broadcastは自身と派生先に向けてイベントを投げます
イベントを区別するための名前と渡したいデータを指定
$broadcast(name, args);
親から$broadcastを投げる
$broadcastをcontroller内で直接読んでも子に伝搬されません、おそらくその時点では子controllerは読み込まれてないからでしょう。
angular.module('myapp', []) .value('printLog', function(event, data) { console.log('event.name:' + event.name) // イベント名 console.log('event.targetScope.text:' + event.targetScope.text); // $emitないし$broadcastした$scopeからtextを取り出す console.log('event.currentScope.text:' + event.currentScope.text); // myControllerの$scopeからtextを取り出す console.log('event.defaultPrevented:' + event.defaultPrevented); // true or false (デフォルトはfalse) console.log('data:' + data); }) .controller('myController', function ($scope, printLog) { $scope.$on('MyEvent', function (event, data) { console.info('myController-MyEvent'); printLog(event, data) }); $scope.text = "myControllerのscope"; this.broadcastMessage = function () { // 画面上からここを発火 $scope.$broadcast('MyEvent', 'myController-$broadcast-MyEvent'); } }) .controller('myControllerChild', function ($scope, printLog) { $scope.$on('MyEvent', function (event, data) { console.info('myControllerChild-MyEvent'); printLog(event, data); }); $scope.text = "myControllerChildのscope"; }) .controller('myControllerGrandChild', function ($scope, printLog) { $scope.$on('MyEvent', function (event, data) { console.info('myControllerGrandChild-MyEvent'); printLog(event, data); }); $scope.text = "myControllerChildGrandchildのscope"; });
// 実行結果 myController-MyEvent event.name:MyEvent event.targetScope.text:myControllerのscope event.currentScope.text:myControllerのscope event.defaultPrevented:false data:myController-$broadcast-MyEvent myControllerChild-MyEvent event.name:MyEvent event.targetScope.text:myControllerのscope event.currentScope.text:myControllerChildのscope event.defaultPrevented:false data:myController-$broadcast-MyEvent myControllerGrandChild-MyEvent event.name:MyEvent event.targetScope.text:myControllerのscope event.currentScope.text:myControllerChildGrandchildのscope event.defaultPrevented:false data:myController-$broadcast-MyEvent
$broadcastの伝搬図
ちゃんと確認は取っていないのですがちょっと動かしてみたところDOMの順で呼ばれていそうな感じ
その他 eventの2つのメソッド
eventの中には以下の4つのプロパティの他に2つのメソッドが存在
- event.stopPropagation(); // イベントの伝搬を止める($emit時のみ使用可能。※$broadcast時はメソッドがない)
- event.preventDefault(); // event.defaultPreventedをtrueにする
event.defaultPreventedはeventの伝搬時にちゃんと受け継ぎます。
dataは途中で書き換えても変わらないのでこれを使って制御可能。ただしtrueとfalseだけ。
細かく制御したい場合は別イベント名にして$emitしなおしてdataに制御用の値を入れるとかになりそう?