diff --git a/src/ngAnimate/animate.js b/src/ngAnimate/animate.js index e66fcd6e1cfc..4178a1acb383 100644 --- a/src/ngAnimate/animate.js +++ b/src/ngAnimate/animate.js @@ -1015,6 +1015,20 @@ angular.module('ngAnimate', ['ng']) } return cache.promise = runAnimationPostDigest(function(done) { + var parentElement; + + //any ancestor elements past $rootElement's are not apart of the + //app therefore there is no need to examine the parent element + if (!isMatchingElement(element, $rootElement)) { + parentElement = element.parent(); + var elementNode = extractElementNode(element); + var parentNode = extractElementNode(parentElement); + if (!parentNode || parentNode['$$NG_REMOVED'] || elementNode['$$NG_REMOVED']) { + done(); + return; + } + } + var cache = element.data(STORAGE_KEY); element.removeData(STORAGE_KEY); @@ -1022,7 +1036,7 @@ angular.module('ngAnimate', ['ng']) var classes = resolveElementClasses(element, cache, state.active); return !classes ? done() - : performAnimation('setClass', classes, element, null, null, function() { + : performAnimation('setClass', classes, element, parentElement, null, function() { $delegate.setClass(element, classes[0], classes[1]); }, done); }); diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js index a571464e2142..a494147acf2d 100644 --- a/test/ngAnimate/animateSpec.js +++ b/test/ngAnimate/animateSpec.js @@ -3413,6 +3413,101 @@ describe("ngAnimate", function() { }); }); + it('should skip class-based animations if the element is removed before the digest occurs', function() { + var spy = jasmine.createSpy(); + module(function($animateProvider) { + $animateProvider.register('.animated', function() { + return { + beforeAddClass : spy, + beforeRemoveClass : spy, + beforeSetClass : spy + }; + }); + }); + inject(function($rootScope, $animate, $compile, $rootElement, $document) { + $animate.enabled(true); + + var one = $compile('
')($rootScope); + var two = $compile('')($rootScope); + var three = $compile('')($rootScope); + + $rootElement.append(one); + $rootElement.append(two); + angular.element($document[0].body).append($rootElement); + + $animate.addClass(one, 'active-class'); + one.remove(); + + $rootScope.$digest(); + expect(spy).not.toHaveBeenCalled(); + + $animate.addClass(two, 'active-class'); + + $rootScope.$digest(); + expect(spy).toHaveBeenCalled(); + + spy.reset(); + $animate.removeClass(two, 'active-class'); + two.remove(); + + $rootScope.$digest(); + expect(spy).not.toHaveBeenCalled(); + + $animate.setClass(three, 'active-class', 'three'); + three.remove(); + + $rootScope.$digest(); + expect(spy).not.toHaveBeenCalled(); + }); + }); + + it('should skip class-based animations if ngRepeat has marked the element or its parent for removal', function() { + var spy = jasmine.createSpy(); + module(function($animateProvider) { + $animateProvider.register('.animated', function() { + return { + beforeAddClass : spy, + beforeRemoveClass : spy, + beforeSetClass : spy + }; + }); + }); + inject(function($rootScope, $animate, $compile, $rootElement, $document) { + $animate.enabled(true); + + var element = $compile( + '