AngularJS directive $destroy
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');
Pwnna
Updated on September 02, 2020Comments
-
Pwnna almost 4 years
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
orviewer
's content. The problem I'm having is a memory leak, where in other directives inside theviewer
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?
-
Pwnna about 11 yearsMy issue is that new i18n directive shows up as I navigate around. So it is not just used once. Consider a case where I have a
<i18n>Last updated</i18n>
in each<ng-view>
. That would be loaded each time I changeng-view
-
Clark Pan about 11 yearsSorry what i meant by the first sentence is that your current implementation would only clean up the last event listener bound. since the
cleanup
var is declared in the 'factory' portion of the directive. The two samples i've provided should do what you want. -
Pwnna about 11 yearsAgreed. That makes sense. An additional step is needed to make this work completely. Refer to: github.com/shuhaowu/osumo/blob/master/static/js/develop/…
-
Ivan almost 9 years@Clark Pan it seems like you use $scope instead of scope in the second example.