7

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?

Community
  • 1
  • 1
CHS
  • 965
  • 2
  • 18
  • 29
  • A similar question with a very useful answer: http://stackoverflow.com/questions/26983696/angularjs-does-destroy-remove-event-listeners – hgoebl Jan 27 '15 at 13:45

1 Answers1

6

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

    }
  }
});
btm1
  • 3,866
  • 2
  • 23
  • 26
  • How are the event handlers on the element w/ the directive attached being removed? – CHS Oct 14 '13 at 11:48
  • You can see I remove any event handlers in the $destroy function and any other events that are attached to the element itself are removed by angular. It's only the ones attached to thinks outside the scope of the directive that you need to remove – btm1 Oct 14 '13 at 14:59
  • Are you sure the element's own listeners don't need to be removed? – CMCDragonkai Mar 09 '14 at 21:09
  • if you specify an event listener within the link function or the controller on the source element of the direct itself than I would remove that even during the destroy event as well yes – btm1 Mar 10 '14 at 20:23