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?
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.
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.
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.
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');
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With