Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do event handlers need to be removed on directives when $destroy fires?

I've seen a lot of directive examples including those by the AngularUI team where they don't appear to do any cleanup.

Here's an example from their ui-date directive which creates a jQuery datepicker. (source)

element.on('blur', function() { ... });

They placed an event handler on the element, but at no point do they ever unbind the event. I would have expected there to be code present such as:

var namespace = ".uiDate";

element.on('blur' + namespace, function() { ... });
element.on("$destroy" + namespace, function ()
{
   element.datepicker("destroy");      //Destroy datepicker widget
   element.off(namespace);             //Unbind events from this namespace
});

So this makes me wonder if there's something I don't understand. Wouldn't what they are doing cause a memory leak in situations where the element w/ this directive is created and destroyed over and over?

What am I missing?

like image 653
CHS Avatar asked Oct 13 '13 14:10

CHS


People also ask

Does removeChild remove event listener?

removeChild(b); b = null; // A reference to 'b' no longer exists // Therefore the element and any event listeners attached to it are removed. However; if there are references that still point to said element, the element and its event listeners are retained in memory.

How do I remove all event listeners?

To remove all event listeners from an element: Use the cloneNode() method to clone the element. Replace the original element with the clone. The cloneNode() method copies the node's attributes and their values, but doesn't copy the event listeners.

What is $destroy in AngularJS?

$destroy. Broadcasted when a scope and its children are being destroyed. Note that, in AngularJS, there is also a $destroy jQuery event, which can be used to clean up DOM bindings before an element is removed from the DOM.


1 Answers

Yes ideally you should clean up any event handlers that are attached to elements other than the element linked to the directive.

Fore example if in your directive you have a window resize function to modify the element of the directive you will need to remove the window resize event when the directive is destroyed.

here's an example directive I had to build and you can see I had to unbind the event handlers attached outside of the scope of the directive:

lrApp.directive('columnArrow',function($timeout){
  return {
    restrict : 'A',
    scope : {
      active : '=columnArrow'
    },
    link: function($scope, elem, attrs, controller) {
        $scope.$watch('active',function(){
          $timeout(function(){
            adjust();
          },0);
        });

        $(window).resize(adjust);

        elem.parents('.column-content').scroll(adjust);

        $scope.$on('$destroy', function () {
          elem.removeClass('hide');
          elem.parents('.column-content').unbind('scroll',adjust);
          $(window).unbind('resize',adjust);
        });

        function adjust(e) {
          if($scope.active) {
            var parentScroll = elem.parents('.column-content');
            var parent = elem.parent();
            var visible = inView(parentScroll[0],parent[0]);
            if(!visible) {
              elem.addClass('hide');
            } else {
              elem.removeClass('hide');
            }
            var offset = parent.offset();
            var w = parent.outerWidth();
            var h = (parent.outerHeight() / 2) - (elem.outerHeight() / 2);
            elem.css({'top':offset.top + h,'left':offset.left + (w + 5)});
          }
        };

    }
  }
});
like image 146
btm1 Avatar answered Sep 28 '22 13:09

btm1