I've built a simple directive that adds a javascript-based loading animation. It is operating with a window.setInterval() loop. This works great, but when loading is complete, I use ngSwitch to swap in my content, which removes the element housing the loading directive attribute from the page.

Ideally, I'd like to watch for this change and clear my interval so the animation calculations are not still running in the background. I have tried watching a custom function that evaluates the presence of the element on the page. I know the function works at detecting this, but it seems timing is an issue -- namely, as far as I can tell, the $watch itself is cleared when the directive attribute's element leaves the page. My $watch'ed expression therefore never detects a change and never calls its callback that clears the animation interval function.

Is there a recommended pattern for dealing with this type of situation?

Relevant snippet from my template:

<div ng-switch on="dataStatus">

  <div ng-switch-when="loading">
    <div loading-spinner></div>
  </div>

  <div ng-switch-when="haveData">
    <!-- data dependent on content we were loading -->
  </div>

</div>

Simplified version of my directive:

myModule.directive('loadingSpinner', function () {
  var updateMySweetAnimation = function (element) { /* ... */ };
  return {
    link: function (scope, iElement, iAttrs) {
      var spinner = window.setInterval(function () {
        updateMySweetAnimation(iElement);
      }, 100);

      scope.$watch(function () {
        return $(document).find(iElement).length;
      }, function (present) {
        if (!present) {
          clearInterval(spinner);
        }
      });
    }
  };
});
有帮助吗?

解决方案

When the element is cleared from the page by ng-switch, two things should happen:

  1. The scope created for ng-switch-when, the element with your directive on, is destroyed. This kills your $watch and generates a $destroy event across the scope that you can watch with scope.$on('$destroy', ...).

  2. The element is removed from the DOM. This generates a separate destroy event that you can watch with iElement.on('$destroy', ...).

They should happen in this order, looking at the latest stable release (1.0.8 - https://github.com/angular/angular.js/blob/v1.0.8/src/ng/directive/ngSwitch.js), so your scope and thus your watch should always be dead when the element is removed from the DOM.

You could avoid this problem by watching from the outer scope, where ng-switch is defined. Or you could watch dataStatus, the same condition as in your ng-switch, rather than looking for the results of the ng-switch seeing your condition change.

Both of these would probably work, but actually all you need to do, and in fact the normal pattern for this, is to just watch for one of the $destroy events and clean everything up there. As the interval feels more relevant to the view than the model, I would use the DOM event and replace your $watch with

iElement.on('$destroy', function(){
    clearInterval(spinner);
});
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top