10

I have directive that bind event using $on do I need to remove that binding when scope is destroyed, or is it done automatically? Also do I need to call $element.off?

return {
    restrict: 'A',
    link: function($scope, $element, $attrs) {
        $element.on('load', function() {
            $element[0].contentWindow.focus();
        });
        $scope.$on('iframe:focus', function() {
            $element[0].contentWindow.focus();
        });
    }
};
jcubic
  • 61,973
  • 54
  • 229
  • 402

4 Answers4

14

$scope.$on() listeners will be destroyed / cleaned up automatically when it loses its representation due to E2E binding in your view. Note that this will not happen for $rootScope.$on() bindings. You could also take a look at the $scope documentation of AngularJS.

Answer in a few words:

  • $scope.$on(); will be destroyed automatically.
  • You need to destroy $rootScope.$on() manually.

The documentation says:

Scope Destruction - When child scopes are no longer needed , it is the responsibility of the child scope creator to destroy them via scope.$destroy() API. This is done in order to stop propagation of $digest calls into the child scope and allow for memory used by the child scope models to be reclaimed by the garbage collector.

Example of how to destroy $rootScope.$on():

// bind event
const registerScope = $rootScope.$on('someEvent', function(event) {
  console.log("fired");
});

// clean up
$scope.$on('$destroy', registerScope);

This plnkr will show you the different behaviors of $scope.$on() and $rootScope.$on().

By switching the view in this plunkr the controller will be rebinded to your view. The $rootScope.$on(); event is binded every time you switch a view without destroying the event bindings of the view before. In that way the $rootScope.$on() listeners will be stacked/multiplied. This will not happen to the $scope.$on() bindings because it will be destroyed by switching the view (losing the E2E binding representation in DOM).


Note that:

  • $scope.$on('event'); will listen to $scope.$broadcast('event') & $rootScope.$broadcast('event')

  • $rootScope.$on('event'); will only listen to $rootScope.$broadcast('event')

developer033
  • 24,267
  • 8
  • 82
  • 108
lin
  • 17,956
  • 4
  • 59
  • 83
4

No, you don't need to remove that binding. It will be removed when the scope is destroyed. However if you bind event to $rootScope always remember to unbind it! It can be done easily like this:

        var unregister = $rootScope.$on('eventName', function(e) {
            //doSomething
        });


        $scope.$on('$destroy', unregister);
Konrad Kahl
  • 177
  • 1
  • 13
3

Scope Destruction - When child scopes are no longer needed , it is the responsibility of the child scope creator to destroy them via scope.$destroy()API. This is done in order to stop propagation of $digest calls into the child scope and allow for memory used by the child scope models to be reclaimed by the garbage collector.

Listeners registered to scopes and elements are automatically cleaned up when they are destroyed, but if you registered a listener on a service, rootScope or registered a listener on a DOM node that isn't being deleted, you'll have to clean it up yourself or you risk introducing a memory leak.

When $scope.$destroy() is executed it will remove all listeners registered via $on on that $scope. It will not remove DOM elements or any attached event handlers added through :

element.on('click', function (event) {
  ...
});

More information about the angular.element https://docs.angularjs.org/api/ng/function/angular.element

daan.desmedt
  • 3,752
  • 1
  • 19
  • 33
0

do I need to remove that binding when scope is destroyed,

The listeners are removed automatically by re-initializing $$listeners. Here is the relevant part from source code:

  $destroy: function() {
    ...
    // Disable listeners, watchers and apply/digest methods
    this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
    this.$on = this.$watch = this.$watchGroup = function() { return noop; };
    this.$$listeners = {};
    ^^^^^^^^^^^^^^^^^^^

Also do I need to call $element.off?

No, they should be removed by a browser when a DOM node associated with $element is destroyed.

Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488