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

또한 docs를 참조하십시오 .


예. sorce 코드를 디버깅 한 후 모든 이벤트가 있고 $ off 함수를 만든 $$ listeners 배열이 있음을 알았습니다. 감사합니다
Hitesh. Aneja

제공된 각 등록 취소 방법을 사용할 수없는 실제 사용 사례는 무엇입니까? 다른 범위에서 등록 취소가 리스너를 생성 한 범위에 연결되어 있지 않습니까?
Liviu T.

1
예, 사람들을 혼동하고 싶지 않기 때문에 실제로 답변을 삭제했습니다. 이것이 올바른 방법입니다.
벤 레시

3
@ Liviu : 응용 프로그램이 증가함에 따라 두통이 될 것입니다. 이 이벤트뿐만 아니라 다른 이벤트도 많으며 항상 동일한 범위 기능으로 등록 취소 해야하는 것은 아닙니다. 이 리스너를 등록하지만 다른 호출에서 리스너를 등록 해제하는 함수를 호출하는 경우가있을 수 있습니다. 내 경우에는 범위 외부에 저장하지 않으면 참조를 얻지 못할 수 있습니다. 따라서 현재 구현의 경우 구현이 실행 가능한 솔루션으로 보입니다. 그러나 AngularJS가 왜 이런 식으로했는지 이유를 알고 싶습니다.
Hitesh. Aneja

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

2
가장 좋은 대답이지만 $ destroy 내에서 해당 리스너를 호출하면 리스너가 종료되는 이유에 대한 자세한 설명을 원합니다.
Mohammad Rafigh 1

1
@MohammadRafigh $ destroy 내부에서 리스너를 호출하는 것은 내가 넣은 곳입니다. 올바르게 기억한다면, 이것은 지시문 안에있는 코드 였고 지시문 범위가 파괴되면 리스너의 등록이 취소되어야한다는 것이 합리적이었습니다.
long2know

@hgoebl 무슨 말인지 모르겠습니다. 예를 들어, 여러 곳에서 사용되는 지시문이 있고 각각 이벤트에 대한 리스너를 등록하는 경우 $ rootScope. $ on을 사용하면 어떻게 도움이됩니까? 지침의 범위 처분은 청취자를 처분하기에 가장 좋은 장소 인 것 같습니다.
long2know

26

이 코드는 저에게 효과적입니다.

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

1
$ rootScope. $$ listeners를 살펴보면 청취자의 수명주기를 관찰하고 실험 할 수 있습니다.
XML

간단하고 훌륭해 보입니다. 방금 기능 참조가 제거되었다고 생각합니다. 그렇지 않습니까?
Jay Shukla

26
$$ listeners 회원은 비공개로 간주되므로이 솔루션은 권장되지 않습니다. 실제로 접두사가 '$$'인 각도 객체의 멤버는 규칙에 따라 비공개입니다.
shovavnik

5
이 옵션은 제거해야 할 청취자뿐만 아니라 모든 청취자를 제거하기 때문에 권장하지 않습니다. 스크립트의 다른 부분에 다른 리스너를 추가하면 나중에 문제가 발생할 수 있습니다.
Rainer Plumer

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
Rick

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 % 정확하기 때문에 실제로 답변을 삭제하고 있습니다.
벤 레시

@blesh LiviuT의 답변은 정확하고 경멸 적으로 제공되는 등록 취소 방법이지만 다른 범위에서 리스너를 등록 취소 해야하는 시나리오에는 제대로 연결되지 않습니다. 따라서 이것은 쉬운 대안입니다.
Hitesh. Aneja

1
다른 솔루션과 동일한 연결을 제공합니다. 파괴 함수를 포함하는 변수를 외부 클로저 또는 전역 컬렉션 또는 원하는 곳에 배치하면됩니다.
벤 레시

등록 취소 함수에 대한 참조를 유지하기 위해 전역 변수를 계속 만들고 싶지 않으며 $ off 함수를 사용하는 데 아무런 문제가 없습니다.
Hitesh. Aneja

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

두 가지 생각 :

  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

한 가지 방법은 리스너를 완료 한 후에 단순히 리스너를 파괴하는 것입니다.

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.