AngularJS'de bir yayın etkinliğinin aboneliğini kaldırma. $ On üzerinden kayıtlı işlev nasıl kaldırılır


278

Dinleyicimi $ on işlevini kullanarak bir $ broadcast etkinliğine kaydettirdim

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

ve belirli bir iş kuralına dayanarak bu dinleyicinin kaydını kaldırmak istiyorum. Ama benim sorunum, kayıt olduktan sonra kayıttan çıkamıyorum.

AngularJS'de belirli bir dinleyicinin kaydını kaldırmak için herhangi bir yöntem var mı? Bu etkinliğin kaydını kaldırmak için $ gibi bir yöntem $ off olabilir. Bu iş mantığına dayanarak söyleyebilirim

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

ve birisi "onViewUpdated" olayını yayınladığında bu fonksiyonun çağrılması durur.

Teşekkürler

EDIT : Dinleyicinin başka bir işlevden kaydını silmek istiyorum. Kaydettiğim işlev değil.


2
Merak eden herkes için, döndürülen işlev burada
Fagner Brack

Yanıtlar:


477

Döndürülen işlevi saklamanız ve olaydan çıkmak için çağırmanız gerekir.

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

Bu kaynak kodunda bulunur :) en az 1.0.4. Kısa olduğu için kodun tamamını yayınlayacağım

/**
  * @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;
    };
},

Ayrıca, dokümanlara bakın .


Evet. Sorce kodunda hata ayıklama sonra tüm olayları olan ve benim $ off işlevi oluşturulan bir $$ listeners dizi olduğunu öğrendim. Teşekkürler
Hitesh.Aneja

Sağlanan açısal kayıt silme işlemini kullanamayacağınız gerçek kullanım durumu nedir? Kayıt silme işlemi başka bir kapsamda mı, dinleyiciyi oluşturan kapsamla bağlantılı değil mi?
Liviu T.

1
Evet, aslında cevabımı sildim çünkü insanları karıştırmak istemiyorum. Bunu yapmanın doğru yolu budur.
Ben Lesh

3
@Liviu: Bu artan uygulama ile baş ağrısı olacak. Sadece bu olay değil, aynı zamanda her zaman aynı kapsam işlevinde de-kayıt olmak başka birçok olay da vardır. Ben bu dinleyiciyi kaydediyor ama diğer çağrıda dinleyicinin kayıttan çıkarılması bir işlevi çağırıyorum durumlarda olabilir, hatta ben bu kapsamda onları kapsamım dışında saklamak sürece referans alamazsınız. Dolayısıyla şu anki uygulamam için benim uygulamam bana uygun bir çözüm gibi görünüyor. Ama kesinlikle AngularJS'in bu şekilde yapmasının nedenlerini bilmek istiyorum.
Hitesh.Aneja

2
Angular bunu bu şekilde yaptığını düşünüyorum çünkü çoğu zaman satır içi anonim işlevleri $ on işlevine argümanlar olarak kullanılır. $ Scope. $ Off (type, function) işlevini çağırabilmek için anonim işleve başvurmamız gerekir. Sadece normalde ActionScript veya Java'daki Gözlenebilir kalıp gibi bir dilde olay dinleyicilerini nasıl ekleyeceğini / kaldıracağını farklı bir şekilde düşünüyor
dannrob

60

Cevapların çoğuna baktığımızda, aşırı derecede karmaşık görünüyorlar. Angular, kaydı silmek için mekanizmalar oluşturmuştur.

Tarafından döndürülen kayıt silme işlevini$on kullanın :

// 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();
});

Bunlar kadar basit, çok cevap var ama bu daha özlü.
David Aguilar

8
Teknik olarak doğru, biraz yanıltıcı olsa da, $scope.$onmanuel olarak kayıtsız kalması gerekmiyor $destroy. Daha iyi bir örnek a $rootScope.$on.
hgoebl

2
en iyi cevap ama bu dinleyiciyi $ destroy içinde çağırmanın neden dinleyiciyi öldürdüğüne dair daha fazla açıklama görmek istiyorum.
Mohammad Rafigh

1
@MohammadRafigh $ destroy içinde dinleyiciyi çağırmak tam da onu koymayı seçtiğim yer. Doğru hatırlıyorsam, bu bir direktifin içinde sahip olduğum koddu ve direktiflerin kapsamı yok edildiğinde dinleyicilerin kayıtsız kalması mantıklıydı.
long2know

@hgoebl Ne demek istediğini bilmiyorum. Örneğin, birden çok yerde kullanılan bir direktifim varsa ve her biri bir olay için bir dinleyici kaydediyorsa, $ rootScope. $ Kullanarak bana nasıl yardımcı olabilirim? Direktifin kapsamı elden çıkarması dinleyicilerini elden çıkarmak için en iyi yer gibi görünmektedir.
long2know

26

Bu kod benim için çalışıyor:

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

1
$ RootScope. $$ dinleyicilerine bakmak dinleyicinin yaşam döngüsünü gözlemlemenin ve denemenin iyi bir yoludur.
XML

Basit ve harika görünüyor. Ben sadece onun işlev kaldırıldı referans düşünüyorum. değil mi?
Jay Shukla

26
$$ dinleyici üyesi özel kabul edildiğinden bu çözüm önerilmez. Aslında, '$$' önekine sahip bir açısal nesnenin herhangi bir üyesi kural gereği özeldir.
shovavnik

5
Bu seçeneği tavsiye etmem, çünkü tüm dinleyicileri kaldırır, sadece kaldırmanız gereken birini değil. Komut dosyasının başka bir bölümüne başka bir dinleyici eklediğinizde gelecekte sorunlara neden olabilir.
Rainer Plumer

10

EDIT: Bunu yapmanın doğru yolu @ LiviuT cevabında!

Bu tür dinleyicileri kaldırmanıza izin vermek için Angular'ın kapsamını her zaman genişletebilirsiniz:

//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);
              }
            }
          }
        }
      }
}());

Ve işleyiş şekli:

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

Ve işte bunun bir dalgıç


Bir cazibe gibi çalıştı! ama biraz düzenledim .run bölümüne koydum
aimiliano

Bu çözümü seviyorum. Daha temiz bir çözüm sağlar - okunması çok daha kolaydır. +1
Rick

7

Kod hata ayıklama sonra, ben sadece "blesh" yanıtı gibi kendi işlevi yarattı. Ben de öyle yaptım

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);
                    }
                }
            }
        }
});

bu yüzden işlevimi $ rootscope'a ekleyerek artık tüm kontrolörlerim tarafından kullanılabilir.

ve kodumda yapıyorum

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

Teşekkürler

EDIT: Bunu yapmak için AngularJS yolu @ LiviuT cevabı var! Ancak, dinleyicinin başka bir kapsamda kaydını silmek ve aynı zamanda kayıttan çıkarma işlevinin referanslarını tutmak için yerel değişkenler oluşturmaktan uzak kalmak istiyorsanız. Bu olası bir çözümdür.


1
Aslında cevabımı siliyorum, çünkü @ LiviuT'un cevabı% 100 doğru.
Ben Lesh

@blesh LiviuT'un cevabı doğrudur ve aslında bir angualar kayıttan çıkmaya yönelik bir yaklaşım sağlamıştır, ancak dinleyiciyi farklı kapsamda kayıttan çıkarmanız gereken senaryolar için iyi bir bağlantı oluşturmaz. Yani bu kolay bir alternatif.
Hitesh.Aneja

1
Başka herhangi bir çözümün aynı kancayı sağlar. İmha fonksiyonunu içeren değişkeni bir dış kapamaya, hatta küresel bir koleksiyona koymuştunuz ... ya da istediğiniz yere.
Ben Lesh

Kayıt kaldırma işlevlerinin referanslarını tutmak için global değişkenler oluşturmaya devam etmek istemiyorum ve ayrıca kendi $ off işlevimi kullanmayla ilgili herhangi bir sorun görmüyorum.
19

1

@ LiviuT'un cevabı harika, ancak yaratıldığı yerden başka bir yerden yok etmek istiyorsanız, işleyicinin yıkılma işlevine başka bir $ kapsamından veya işlevinden nasıl yeniden erişileceğini merak eden birçok insan bırakıyor gibi görünüyor. @ Рустем Мусабеков'in yanıtı çok iyi, ama çok deyimsel değil. (Ve her zaman değişebilecek özel bir uygulama detayı olması gerekenlere dayanır.) Ve oradan, daha da karmaşıklaşıyor ...

Buradaki kolay cevabın offCallMeFn, işleyicinin kendisinde parçalama işlevine ( örneğinde) bir referans taşımak ve daha sonra bazı koşullara göre çağırmak olduğunu düşünüyorum; belki de $ broadcast veya $ emit ettiğiniz etkinliğe dahil ettiğiniz bir argüman. Böylece işleyiciler, istedikleri zaman, istediğiniz yerde, kendi imha tohumlarını taşıyarak kendilerini parçalayabilirler. Şöyle ki:

// 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.)

İki düşünce:

  1. Bu, bir kez çalıştırılan işleyici için harika bir formüldür. Şartları bırakın selfDestructve intihar görevini tamamlar tamamlamaz koşun .
  2. Kapatılan değişkenlere referanslar taşıyorsanız, kaynak kapsamının hiç uygun şekilde yok edilip edilmeyeceğini ve çöp toplanıp toplanmayacağını merak ediyorum. Bir hafıza problemi olsa bile bunlardan bir milyonunu kullanmalısın, ama merak ediyorum. Herhangi bir görüş varsa, lütfen paylaşın.

1

Bileşen kaldırıldığında dinleyicilerinizin aboneliğini iptal etmek için bir kanca kaydedin:

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

Bunu yapmanın genel olarak kabul edilen yolu olmasa da, bunun gerekli çözüm olduğu zamanlar vardır.
Tony Brasunas

1

Dinleyiciyi birkaç kez açıp kapatmanız gerektiğinde, booleanparametre ile bir işlev oluşturabilirsiniz.

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

0

'$ on' kendisi kayıt silme işlevini döndürür

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

dinleyicinin kaydını silmek için unregister () işlevini çağırabilirsiniz


0

Bir yol, işiniz bittiğinde dinleyiciyi yok etmektir.

var removeListener = $scope.$on('navBarRight-ready', function () {
        $rootScope.$broadcast('workerProfile-display', $scope.worker)
        removeListener(); //destroy the listener
    })
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.