angularJSでブロードキャストイベントの登録を解除する方法。$ onで登録された関数を削除する方法


278

$ on関数を使用して$ broadcastイベントにリスナーを登録しました

$scope.$on("onViewUpdated", this.callMe);

また、特定のビジネスルールに基づいて、このリスナーの登録を解除したいと考えています。しかし、私の問題は、いったん登録されると、登録を解除できないことです。

AngularJSには、特定のリスナーの登録を解除する方法はありますか?このイベントを登録解除する$ onのようなメソッドは、$ offになる場合があります。ビジネスロジックに基づいて

 $scope.$off("onViewUpdated", this.callMe);

この関数は、誰かが「onViewUpdated」イベントをブロードキャストすると呼び出されなくなります。

ありがとう

編集:リスナーを別の関数から登録解除したい。登録する機能ではありません。


2
疑問に思う方のために、返された関数はここに
Fagner Brack

回答:


477

イベントからサブスクライブを解除するには、返された関数を保存して呼び出す必要があります。

var deregisterListener = $scope.$on("onViewUpdated", callMe);
deregisterListener (); // this will deregister that listener

これはソースコードにあります:)少なくとも1.0.4では。短いので、完全なコードを投稿します

/**
  * @param {string} name Event name to listen on.
  * @param {function(event)} listener Function to call when the event is emitted.
  * @returns {function()} Returns a deregistration function for this listener.
  */
$on: function(name, listener) {
    var namedListeners = this.$$listeners[name];
    if (!namedListeners) {
      this.$$listeners[name] = namedListeners = [];
    }
    namedListeners.push(listener);

    return function() {
      namedListeners[indexOf(namedListeners, listener)] = null;
    };
},

また、ドキュメントを参照してください。


はい。ソースコードをデバッグした後、すべてのイベントを含み、$ off関数を作成した$$ listeners配列があることがわかりました。ありがとう
Hitesh.Aneja

提供されている角度登録解除方法を使用できない実際の使用例は何ですか?登録解除は、リスナーを作成したスコープにリンクされていない別のスコープで行われますか?
Liviu T.

1
ええ、人を混乱させたくないので、私は実際に私の答えを削除しました。これはこれを行う適切な方法です。
Ben Lesh 2013

3
@Liviu:それはアプリケーションの成長とともに頭痛の種になるでしょう。このイベントだけでなく、他にもたくさんのイベントがあり、必ずしも同じスコープ関数で登録解除しているとは限りません。このリスナーを登録しているが、他の呼び出しでリスナーの登録を解除する関数を呼び出す場合があります。それらの場合でも、スコープ外に保存しない限り、参照を取得できません。したがって、現在の実装では、私の実装は私にとって実行可能な解決策に見えます。しかし、AngularJSがこのようにした理由を必ず知りたいと思います。
Hitesh.Aneja 2013

2
多くの場合、インラインの匿名関数が$ on関数の引数として使用されるため、Angularはこのようにしたと思います。$ scope。$ off(type、function)を呼び出すには、無名関数への参照を保持する必要があります。それは、ActionScriptのような言語やJavaのObservableパターンでイベントリスナーを通常追加/削除する方法とは別の方法で考えています
dannrob

60

ほとんどの返信を見ると、それらは過度に複雑に見えます。Angularには、登録を解除するメカニズムが組み込まれています。

によって返される登録解除関数を$on使用します

// Register and get a handle to the listener
var listener = $scope.$on('someMessage', function () {
    $log.log("Message received");
});

// Unregister
$scope.$on('$destroy', function () {
    $log.log("Unregistering listener");
    listener();
});

これらと同じくらい簡単ですが、答えはたくさんありますが、これはより簡潔です。
David Aguilar

8
$scope.$onは手動で登録解除する必要がないため、少し誤解を招きますが、技術的には正しい$destroyです。より良い例は、を使用すること$rootScope.$onです。
hgoebl 2016

2
最良の答えですが、$ destroy内でそのリスナーを呼び出すとリスナーが停止する理由についての詳細な説明を見たいと思います。
Mohammad Rafigh

1
@MohammadRafigh $ destroy内でリスナーを呼び出すことは、私がそれを置くことを選択した場所です。私が正しく思い出せば、これはディレクティブ内にあるコードであり、ディレクティブスコープが破棄されたときにリスナーを登録解除する必要があることは理にかなっています。
long2know

@hgoeblどういう意味かわかりません。たとえば、複数の場所で使用されるディレクティブがあり、それぞれがイベントのリスナーを登録している場合、$ rootScope。$ onを使用するとどのように役立ちますか?ディレクティブのスコープの処分は、そのリスナーを処分するのに最適な場所のようです。
long2know

26

このコードは私にとってはうまくいきます:

$rootScope.$$listeners.nameOfYourEvent=[];

1
$ rootScope。$$ listenersを確認することも、リスナーのライフサイクルを観察し、それを試す良い方法です。
XML

シンプルで素晴らしいですね。関数の参照を削除しただけだと思います。ね?
Jay Shukla 14年

26
$$ listenersメンバーはプライベートと見なされるため、このソリューションは推奨されません。実際、「$$」接頭辞が付いた角度付きオブジェクトのメンバーはすべて、慣例によりプライベートです。
shovavnik 2014年

5
削除する必要があるリスナーだけでなく、すべてのリスナーが削除されるため、このオプションはお勧めしません。スクリプトの別の部分に別のリスナーを追加すると、将来的に問題が発生する可能性があります。
Rainer Plumer 2015

10

編集:これを行う正しい方法は@LiviuTの答えです!

Angularのスコープをいつでも拡張して、そのようなリスナーを次のように削除できます。

//A little hack to add an $off() method to $scopes.
(function () {
  var injector = angular.injector(['ng']),
      rootScope = injector.get('$rootScope');
      rootScope.constructor.prototype.$off = function(eventName, fn) {
        if(this.$$listeners) {
          var eventArr = this.$$listeners[eventName];
          if(eventArr) {
            for(var i = 0; i < eventArr.length; i++) {
              if(eventArr[i] === fn) {
                eventArr.splice(i, 1);
              }
            }
          }
        }
      }
}());

そして、これがどのように機能するかです:

  function myEvent() {
    alert('test');
  }
  $scope.$on('test', myEvent);
  $scope.$broadcast('test');
  $scope.$off('test', myEvent);
  $scope.$broadcast('test');

そして、ここにそれのプランカーがあります


魅力のように働いた!少し編集して.runセクションに
追加しました

このソリューションが大好きです。よりクリーンなソリューションになります-非常に読みやすくなります。+1
リック

7

コードをデバッグした後、「blesh」の答えと同じように自分の関数を作成しました。これは私がやったことです

MyModule = angular.module('FIT', [])
.run(function ($rootScope) {
        // Custom $off function to un-register the listener.
        $rootScope.$off = function (name, listener) {
            var namedListeners = this.$$listeners[name];
            if (namedListeners) {
                // Loop through the array of named listeners and remove them from the array.
                for (var i = 0; i < namedListeners.length; i++) {
                    if (namedListeners[i] === listener) {
                        return namedListeners.splice(i, 1);
                    }
                }
            }
        }
});

私の関数を$ rootscopeにアタッチすると、すべてのコントローラーで使用できるようになります。

そして私のコードで私はやっています

$scope.$off("onViewUpdated", callMe);

ありがとう

編集:これを行うAngularJSの方法は@LiviuTの答えです!ただし、別のスコープでリスナーを登録解除すると同時に、登録解除関数の参照を維持するためにローカル変数を作成しないようにする場合は、これは可能な解決策です。


1
@LiviuTの回答は100%正しいため、実際には私の回答を削除しています。
Ben Lesh 2013

@blesh LiviuTの答えは正確であり、登録を解除するためのアグアラー提供のアプローチですが、別のスコープでリスナーを登録解除する必要があるシナリオではうまく機能しません。したがって、これは簡単な代替手段です。
Hitesh.Aneja 2013

1
他のソリューションと同じフックアップを提供します。破壊関数を含む変数を外部クロージャまたはグローバルコレクションに配置するだけです...または、必要な場所に配置します。
Ben Lesh 2013

登録解除関数の参照を保持するためにグローバル変数を作成し続けたくありません。また、独自の$ off関数を使用しても問題が発生しません。
Hitesh.Aneja 2013

1

@LiviuTの答えは素晴らしいですが、作成された場所以外の場所から破壊したい場合は、ハンドラーの分解関数を別の$ scopeまたは関数から再アクセスする方法について多くの人が疑問に思っているようです。@РустемМусабековの回答はうまくいきますが、あまり慣用的ではありません。(そして、いつでも変更される可能性のあるプライベートな実装の詳細であることに依存しています。)そして、そこから、それはもっと複雑になります...

ここでの簡単な答えはoffCallMeFn、ハンドラー自体に(彼の例では)ティアダウン関数への参照を保持し、いくつかの条件に基づいて呼び出すことだと思います。$ broadcastまたは$ emitするイベントに含める引数。ハンドラーはこのようにして、いつでもどこでも好きなときに自分自身を破壊することができます。そのようです:

// Creation of our handler:
var tearDownFunc = $rootScope.$on('demo-event', function(event, booleanParam) {
    var selfDestruct = tearDownFunc;
    if (booleanParam === false) {
        console.log('This is the routine handler here. I can do your normal handling-type stuff.')
    }
    if (booleanParam === true) {
        console.log("5... 4... 3... 2... 1...")
        selfDestruct();
    }
});

// These two functions are purely for demonstration
window.trigger = function(booleanArg) {
    $scope.$emit('demo-event', booleanArg);
}
window.check = function() {
    // shows us where Angular is stashing our handlers, while they exist
    console.log($rootScope.$$listeners['demo-event'])
};

// Interactive Demo:

>> trigger(false);
// "This is the routine handler here. I can do your normal handling-type stuff."

>> check();
// [function] (So, there's a handler registered at this point.)  

>> trigger(true);
// "5... 4... 3... 2... 1..."

>> check();
// [null] (No more handler.)

>> trigger(false);
// undefined (He's dead, Jim.)

2つの考え:

  1. これは、1回限りのハンドラの優れた式です。条件付きのものをドロップselfDestructし、自殺ミッションが完了したらすぐに実行します。
  2. クロージャ変数への参照を持っている場合、元のスコープが適切に破棄されてガベージコレクションされるかどうかは疑問です。あなたはそれをメモリの問題であるためにこれらの百万を使用しなければならないでしょうが、私は好奇心が強いです。誰か洞察力があれば教えてください。

1

コンポーネントが削除されたときにリスナーを登録解除するフックを登録します。

$scope.$on('$destroy', function () {
   delete $rootScope.$$listeners["youreventname"];
});  

これを行う一般的に受け入れられている方法ではありませんが、これが必要な解決策になる場合があります。
Tony Brasunas

1

リスナーを複数回オン/オフにする必要がある場合は、booleanパラメーターを指定して関数を作成できます

function switchListen(_switch) {
    if (_switch) {
      $scope.$on("onViewUpdated", this.callMe);
    } else {
      $rootScope.$$listeners.onViewUpdated = [];
    }
}

0

'$ on'自体が登録解除の関数を返します

 var unregister=  $rootScope.$on('$stateChangeStart',
            function(event, toState, toParams, fromState, fromParams, options) { 
                alert('state changing'); 
            });

unregister()関数を呼び出して、そのリスナーを登録解除できます


0

1つの方法は、リスナーを使い終わったら、リスナーを破棄することです。

var removeListener = $scope.$on('navBarRight-ready', function () {
        $rootScope.$broadcast('workerProfile-display', $scope.worker)
        removeListener(); //destroy the listener
    })
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.