title: AngularJS: Directive Scope is not getting $destroy called
body: <p>Been searching for awhile in the archives and on the 'net for an answer, but not really finding one, just bits and pieces. Seems there's a lot of suggested posts, but none of those have the answer.</p>
  
  <p>I have a complex directive which is using scope: true.  It's actually the ng-grid latest 2.x version that I'm trying to write cleanup code for, since it just is leaking memory like crazy, and our app is "stuck" with it for now. Here's <a href="http://plnkr.co/edit/BMoCp4sRcdq6KgguvqGz" rel="noreferrer">the plunk that demos the problem</a>. When you click through the grid and check the heap snapshots afterwards, you'll see several "unattached" ngGrid objects. These are only held down by listeners on the directive's scope.</p>
  
  <p>When I'm changing states (using ui.route latest) by clicking through the multiple grids (states), the grid directive's scope IS getting the $destroy event. The handler is working. However, the scope itself is NOT getting $destroy() called on it.  I see in the heap snapshot that the directive's scope is still holding onto elements via $$listeners.  Also, scope.$$destroyed is not set.</p>
  
  <p>However, scope.<em>prototype</em> <em>IS</em> destroyed.  And because it is, I can't even call $scope.$destroy from the directive's $on-$destroy handler, since the proto's $destroy() call changed the definition of $destroy to noop:</p>
  
  <pre><code>ngGridDirectives.directive('ngGrid', ['$compile', '$filter', '$templateCache', '$sortService', '$domUtilityService', '$utilityService', '$timeout', '$parse', '$http', '$q',
      function($compile, $filter, $templateCache, sortService, domUtilityService, $utils, $timeout, $parse, $http, $q) {
    var ngGridDirective = {
      scope: true,
      compile: function() {
        return {
          pre: function($scope, iElement, iAttrs) {
            $(iElement).on('$destroy', function cleanupElementNgGrid() {
                $timeout(function() {
                  $scope.$destroy();
                  $scope.destroyedByForce = true;
                  console.log("I destroyed it.");
                },4000);
            });
          ...
  </code></pre>
  
  <p>Setting scope.$$listeners = {} with a $timeout of 2000 (to give time for the directive to finish cleaning up in my on-$destroy listener seems to work , but it feels wrong to munge with the internals, and sometimes it's not long enough for the browser to finish the cleanup.  It's just a work-around/kludge.</p>
  
  <p>I also tried this:</p>
  
  <p>So what is preventing my directive's scope from getting $destroy() automatically called on it?</p>
  
  <p>At first, I thought it was because we use scope: true in the directive options, since the proto scope seems to have been destroyed. So I wrote <a href="http://plnkr.co/edit/ucqlRC" rel="noreferrer">a plunk to try that theory out</a>. But with this plunk, the directive's scope is properly destroyed and no objects are leaked. Very surprising, actually. But I don't use the same view nesting I have in the first plunk; however I doubt that's it.  Controller scope still gets wiped when the views change. I still have a watcher on an internal object. So I would think I'd see a similar dynamic. But it appears that $destroy is really called on that internal scope.</p>
  
  <p>Any ideas about what would prevent a directive scope from getting $destroy() called on it? IT seems related to the scope: true bit, but I can't quite grok Angular internals enough to say why.</p>
  
  <p>Thank you in advance,</p>
  
  <p>Jesse</p>
  
  <hr>
  
  <p>Update: OK, I'll post an update to this issue.</p>
  
  <p>To the best of my ability, it seems what's happening is that the main scope for the element (not the semi-isolate scope for the grid directive) gets $destroy()d.  However, the child scope which is using that scope as the prototype is most likely bailing out of the $destroy method early, because self.$$destroyed is true (prototypal inheritance). So listeners and watchers do not get cleared.</p>
  
  <p>I probably could update the directive to be fully isolate in scope, but I don't have time to sort out the implications of that.</p>
  
  <p>I also found it's more than just $destroy.  The $scope itself has a LOT of functions defined on it by the directive that form closure around internal vars that hold a lot of memory. </p>
  
  <p>So, I wrote a service that supplies additional cleanup that is easily injected into the questionable grid directive. I have a plunk demo'ing it, but I can't post it in the main piece due to my low reputation :).  Now, you can switch grids all you like and none but the current grids will remain on the heap.</p>
  
  <p>Hope this helps someone going forward,</p>
  
  <p>J</p>