Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS directive $destroy

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?

like image 320
Pwnna Avatar asked Jun 19 '13 23:06

Pwnna


People also ask

What is directive in AngularJS?

AngularJS directives are extended HTML attributes with the prefix ng- . The ng-app directive initializes an AngularJS application. The ng-init directive initializes application data. The ng-model directive binds the value of HTML controls (input, select, textarea) to application data.

What is restrict in AngularJS directive?

Note: When you create a directive, it is restricted to attribute and elements only by default. In order to create directives that are triggered by class name, you need to use the restrict option. The restrict option is typically set to: 'A' - only matches attribute name.

What is custom directive in AngularJS?

Custom directives are used in AngularJS to extend the functionality of HTML. Custom directives are defined using "directive" function. A custom directive simply replaces the element for which it is activated.


1 Answers

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'); 
like image 133
Clark Pan Avatar answered Sep 20 '22 23:09

Clark Pan