Pregunta Directiva AngularJS $ destroy


Tengo una configuración de aplicación angular con ng-view. En una vista, además de la vista en sí, también hay un componente dentro de esa vista que se carga dinámicamente. Este componente es una directiva que básicamente compila los contenidos para que los contenidos puedan engancharse con otras directivas (lo cual es). El contenido dentro de ese componente se compila usando $compile(element.contents())(scope);.

Como ejemplo:

<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);
      }
    );
  };
}]);

Mi problema es que cuando cambio de ruta, esencialmente cambio ng-view oviewercontenido. El problema que tengo es una pérdida de memoria, donde en otras directivas dentro del viewer se engancha a los eventos y no se limpia cuando se cambia la ruta.

Un ejemplo de esto es el siguiente:

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();
      });
    }
  };
}]);

¿Cómo lo tengo para que estos eventos se limpien adecuadamente?

Gracias.


32
2018-06-19 23:28


origen


Respuestas:


El ejemplo i18n que proporcionaste funcionaría si solo lo usaras una vez.

No creo que deba estar haciendo el enlace del evento dentro de la función de compilación. Puedes hacerlo dentro de la función de enlace:

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();
      });
    }
  };
}]);

Alternativamente, podría vincular el evento en el propio alcance del niño y usar $ broadcast en $ rootScope para activarlo. De esta forma, el evento será basura automáticamente cuando se destruya el alcance:

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');

45
2018-06-19 23:44