AngularJS directive $destroy

断了今生、忘了曾经 提交于 2019-12-20 10:18:14

问题


I have an angular app setup with ng-view. In one view, in addition to the view itself, there is also a component inside that view that is dynamically loaded. This component is a directive that essentially compiles the contents so the contents can be further hooked with other directives (which it is). The content inside that component is compiled using $compile(element.contents())(scope);.

As an example:

<ng-view>
  <viewer doc="getDocument()">
  </viewer>
</ng-view>
angular.directive('viewer', ['$compile', '$anchorScroll', function($compile, $anchorScroll) {
  return function(scope, element, attrs) {
    scope.$watch(
      function(scope) {
        var doc = scope.$eval(attrs.doc);
        if (!doc)
          return ""
        return doc.html;
      },
      function(value) {
        element.html(value);
        $compile(element.contents())(scope);
      }
    );
  };
}]);

My problem is when I switch routes, I essentially switch ng-view or viewer's content. The problem I'm having is a memory leak, where in other directives inside the viewer hooks to events and do not clean up when the route is changed.

One such example is as follows:

angular.directive('i18n', ['$rootScope', 'LocaleService', function($rootScope, LocaleService) {
  var cleanup;
  return {
    restrict: 'EAC',
    compile: function(element, attrs) {
      var originalText = element.text();
      element.text(LocaleService.getTranslation(originalText, attrs.locale));
      cleanup = $rootScope.$on('locale-changed', function(locale) {
        element.text(LocaleService.getTranslation(originalText, attrs.locale || locale));
      });
    },
    link: function(scope) {
      scope.$on('$destroy', function() {
        console.log("destroy");
        cleanup();
      });
    }
  };
}]);

How do I have it so that these events are properly cleaned up?

Thanks.


回答1:


The i18n example you provided would work if you only ever used it once.

I don't think you should be doing the event binding inside the compile function. You can do it inside the link function instead:

angular.directive('i18n', ['$rootScope', 'LocaleService', function($rootScope, LocaleService) {
  return {
    restrict: 'EAC',
    link: function(scope, element, attrs) {
      var cleanup;
      var originalText = element.text();
      element.text(LocaleService.getTranslation(originalText, attrs.locale));
      cleanup = $rootScope.$on('locale-changed', function(locale) {
        element.text(LocaleService.getTranslation(originalText, attrs.locale || locale));
      });
      scope.$on('$destroy', function() {
        console.log("destroy");
        cleanup();
      });
    }
  };
}]);

Alternatively, you could bind the event on the child scope itself, and use $broadcast on the $rootScope to trigger it. That way the event will automatically be garbage collected when the scope is destroyed:

angular.directive('i18n', ['$rootScope', 'LocaleService', function($rootScope, LocaleService) {
  return {
    restrict: 'EAC',
    link: function(scope, element, attrs) {
      var originalText = element.text();
      setElText();
      function setElText(locale){
        element.text(LocaleService.getTranslation(originalText, attrs.locale || locale));
      }
      scope.$on('locale-changed', setElText);
    }
  };
}]);

$rootScope.$broadcast('locale-change', 'en-AU');


来源:https://stackoverflow.com/questions/17203005/angularjs-directive-destroy

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!