1. ホーム
  2. scope

[解決済み] AngularJSでコントローラ間の通信を行う正しい方法は何ですか?

2022-03-20 02:12:45

質問

コントローラ間の通信はどのように行うのが正しいのでしょうか?

現在、私は以下のようなひどいごまかしを使っています。 window :

function StockSubgroupCtrl($scope, $http) {
    $scope.subgroups = [];
    $scope.handleSubgroupsLoaded = function(data, status) {
        $scope.subgroups = data;
    }
    $scope.fetch = function(prod_grp) {
        $http.get('/api/stock/groups/' + prod_grp + '/subgroups/').success($scope.handleSubgroupsLoaded);
    }
    window.fetchStockSubgroups = $scope.fetch;
}

function StockGroupCtrl($scope, $http) {
    ...
    $scope.select = function(prod_grp) {
        $scope.selectedGroup = prod_grp;
        window.fetchStockSubgroups(prod_grp);
    }
}

解決方法は?

編集 : この回答で取り上げられた問題は、angular.jsで解決されました。 バージョン1.2.7 . $broadcast が、未登録スコープでのバブリングを回避し、$emitと同等の速度で実行されるようになりました。

だから、今はできるんです。

  • 使用 $broadcast から $rootScope
  • を使って聴く $on ローカルから $scope イベントについての情報を必要とする

以下、オリジナル回答

を使用しないことを強くお勧めします。 $rootScope.$broadcast + $scope.$on ではなく $rootScope.$emit + $rootScope.$on . 前者は @numan が指摘したように、深刻なパフォーマンスの問題を引き起こす可能性があります。というのも、このイベントは すべて スコープを使用します。

しかし、後者(using $rootScope.$emit + $rootScope.$on ) があります。 ではない そのため、高速な通信チャネルとして使用することができます。

のアンギュラードキュメントから $emit :

イベント名をスコープ階層を通して上方にディスパッチし、登録された

より上位のスコープは存在しないので $rootScope バブリングは起こりません。を使っても全く問題ありません。 $rootScope.$emit() / $rootScope.$on() をEventBusとする。

ただし、Controller内から使用する場合は、1つだけ注意点があります。もし、直接 $rootScope.$on() がコントローラ内にある場合は、ローカルの $scope は破棄されます。これは、(サービスとは対照的に)コントローラはアプリケーションのライフタイム中に何度もインスタンス化される可能性があり、その結果バインディングの合計がメモリリークを引き起こすことになるからです :)

登録を解除するには $scope 's $destroy イベントから返された関数を呼び出します。 $rootScope.$on .

angular
    .module('MyApp')
    .controller('MyController', ['$scope', '$rootScope', function MyController($scope, $rootScope) {

            var unbind = $rootScope.$on('someComponent.someCrazyEvent', function(){
                console.log('foo');
            });

            $scope.$on('$destroy', unbind);
        }
    ]);

リソースをクリーンアップしなければならないというのは、他のEventBusの実装にも当てはまることなので、angularに限ったことではないのですが、そう言いたいです。

しかし、あなたは できる そのような場合のために、自分の生活を楽にする。例えば、モンキーパッチ $rootScope を与えて、それを $onRootScope で発行されるイベントを購読するものです。 $rootScope が、ローカルの $scope は破棄される。

をモンキーパッチする最もクリーンな方法です。 $rootScope を提供するために、このような $onRootScope メソッドは、デコレータを使うことになります (実行ブロックでもうまくいくでしょうが、内緒です)。

を確認するために $onRootScope プロパティを列挙したときに予期せぬものが表示されないようにするためです。 $scope を使用します。 Object.defineProperty() を設定し enumerable から false . ES5 shimが必要になるかもしれないことを念頭に置いてください。

angular
    .module('MyApp')
    .config(['$provide', function($provide){
        $provide.decorator('$rootScope', ['$delegate', function($delegate){

            Object.defineProperty($delegate.constructor.prototype, '$onRootScope', {
                value: function(name, listener){
                    var unsubscribe = $delegate.$on(name, listener);
                    this.$on('$destroy', unsubscribe);

                    return unsubscribe;
                },
                enumerable: false
            });


            return $delegate;
        }]);
    }]);

この方法を用いると、上記のコントローラのコードは次のように簡略化されます。

angular
    .module('MyApp')
    .controller('MyController', ['$scope', function MyController($scope) {

            $scope.$onRootScope('someComponent.someCrazyEvent', function(){
                console.log('foo');
            });
        }
    ]);

ということで、最終的には $rootScope.$emit + $scope.$onRootScope .

Btw、私はangularチームにangularコア内でこの問題に対処するよう説得しています。ここで議論が行われています。 https://github.com/angular/angular.js/issues/4574

以下は、perfの影響がどの程度かを示すjsperfです。 $broadcast を100個だけ使ったまともなシナリオでは $scope 's.

http://jsperf.com/rootscope-emit-vs-rootscope-broadcast