Hatena::ブログ(Diary)

Kazzzの日記 このページをアンテナに追加 RSSフィード

2014-08-09

[]コントローラ間のイベント通知では$broadcastが使えない場合がある

以前、AngularJSで複数のコントローラ間でデータを共有する手段として、$broadcastを使ったイベントの通知を紹介したことがある。
[AngularJS][Javascript]他の要素に変更を通知する

これならスマートにデータを共有できる。しかし、この方法を様々に使って見ると想定通りの動きをしないケースがあることが分かった。

サンプルとしては、1年前のエントリ同様にDOM上で親子関係を持つ二つのコントローラ、があるとしよう。

parentController.js
function ParentController($scope, $broadcast, $log) {
    //リソース取得後にブロードキャスト
   $scope.user = User.getUserFromResource( {},
        function(data){
            $scope.user = data;
            $scope.$broadcast('event:userReceived', data);
        }
    });
}

ParentControllerでは$resource等を使って外部リソース取得後に$broadcastで他のコントローラにリソース取得を通知する。なので$broadcastを実行する際にChildControllerはロードされた後に$onが実行されて$broadcastを受ける準備が整っている必要がある訳だ。

childController.js
function ChildController($scope, $log) {
    $scope.$log = $log;
    $scope.$on('event:userReceived', function(event, data) {
        $scope.$log.log('++++ userReceived broadcast');
    });
}

しかし、例えばChildControllerがng-viewディレクティブでロードされる構造になっている場合、親のコントローラの間でロード(ダイジェスト)の順序が変わるのか、1度目は問題無いのに、その後、Webブラウザによりページをリロードした場合には、ChildController中の$onの実行($broadcastの監視開始)が$broadcastより遅れてしまうことがある。その場合、ChildControllerの$onは決して呼ばれない。

このケースのように、子のコントローラで親のコントローラのイベントを確実にハンドルした場合は、子のコントローラの準備が出来たことを親コントローラが知る必要がある。

このようなケースでは$emitを使う。

childController.js (改)
function ChildController($scope, $log) {
    $scope.$log = $log;
    $scope.$on('event:userReceived', function(event, data) {
        $scope.$log.log('++++ userReceived broadcast');
    });
    //自身がロードを完了したことを親に通知する
    $scope.$emit('event:onChildControllerLoaded', this);
}

parentController.js (改)
function ParentController($scope, $broadcast, $log) {
   $scope.$on("event:onChildControllerLoaded", function(event, controller){
        $scope.user = User.getUserFromResource({},
            function(data){
                $scope.user = data;
                //リソース取得後にブロードキャスト
               $scope.$broadcast('event:userReceived', $scope.user);
            }
        });
   }
}

ちょっと面倒だが、これでChildControllerはParentControllerの$broadcastを受けることができる。

トラックバック - http://d.hatena.ne.jp/Kazzz/20140809/p1
リンク元