angularは変数の共有がなかなか慣れるまで面倒くさい。
詰まったポイントをメモします。
要件
- 異なるコントローラー間で変数を受け渡したい
- そのために変数共有用のgetter/setterを実装したserviceを作成
- serviceを通して変数を共有する
NGだったパターン
ファイルは4つ。
- haml:template.html.haml
- coffeescript:shared_number.js.coffee
- coffee:parent_controller.js.coffee
- coffee:child_controller.js.coffee
-# SET-NUMBERをクリックすると、直下のdivのchild_numに番号が表示されることを期待
%div( ng-controller='ParentController')
%a( ng-click='set_child_num(69)' ) SET-NUMBER!
%div( ng-controller='ChildController')
{{child_num}}
App.factory 'SharedNumberFactory', ['$rootScope', ($rootScope) ->
number = ''
return{
number:{
get: ()->
return parseInt(number)
set: (i)->
number = i
$rootScope.$broadcast('event:onSetNumber')
}
}
]
App.controller "ParentController", ($scope, SharedNumberFactory) ->
$scope.set_child_num = (num) ->
SharedNumberFactory.number.set(num)
App.controller "ChildController", ($scope, SharedNumberFactory) ->
$scope.$on('event:onSetNumber', () ->
$scope.child_num = SharedNumberFactory.number.get()
console.log 'set child_num'
と記述した。
イメージしたプロセスは以下のとおり
- 1.ParentControllerのset_child_num をng-clickで実行。
- SharedNumberFactory内のset(num)で、SharedNumberFactoryに番号をセット。
- 2.SharedNumberFactoryで番号をsetし、'event:onSetNumber'リスナーを登録しているオブジェクトに波及させる
- 3.ChildControllerには'event:onSetNumber'が登録されているので、その内部が実行され、child_numに数字が登録される
だが、
2.SharedNumberFactoryで番号をsetし、'event:onSetNumber'リスナーを登録しているオブジェクトに波及させる
が実行されない。
どうやらリスナーとして、ChildControllerに'event:onSetNumber'が登録されていない模様。
Factoryの$broadcast
を実行する前に、
どういうわけか、
lisnerとして'event:onSetNumber'が登録されていないのが原因の模様。
たとえば、Factoryやserviceを使わずとも
親controllerに$broadcast
を実行し、子controllerで$onを登録しているだけのパターンでも
その減少が起こりうる場合があるとのこと。
OKパターン
(参考) [AngularJS]コントローラ間のイベント通知では$broadcastが使えない場合がある
http://d.hatena.ne.jp/Kazzz/20140809/p1
上記アドレスを参考に下記のようなコードに書き換えた。
書き換えたファイルは
- coffee:shared_number.js.coffee
- coffee:child_controller.js.coffee
の2つ
App.factory 'SharedNumberFactory', ['$rootScope', ($rootScope) ->
number = ''
return{
number:{
get: ()->
return parseInt(number)
set: (i)->
number = i
$rootScope.$on 'event:onControllerLoaded', () ->
$rootScope.$broadcast('event:onSetNumber') //★追加
}
}
]
App.controller "ChildController", ($scope, SharedNumberFactory) ->
$scope.$on('event:onSetNumber', () ->
$scope.child_num = SharedNumberFactory.number.get()
console.log 'set child_num'
$scope.$emit('event:onControllerLoaded', this) //★追加
大きな変更は2つ。
ChildControllerが読み出されたタイミングで$emit()
を利用して、
-
$scope
に登録されているリスナー'event:onControllerLoaded'に自身の存在を告知。 - その中に
$broadcast()
メソッドを登録する
これでFactory/Serviceや親コントローラに自身の存在を登録させる事ができる。
参考
[AngularJS]コントローラ間のイベント通知では$broadcastが使えない場合がある
http://d.hatena.ne.jp/Kazzz/20140809/p1
Angular JS で複数のコントローラ間でモデル(状態や値)を共有する方法 3 種類
http://qiita.com/sunny4381/items/aeae1e154346b5cf6009