From 77419cf19fe625b262e971d5453151c63ff52b34 Mon Sep 17 00:00:00 2001 From: Martin Staffa Date: Tue, 1 Dec 2015 18:04:16 +0100 Subject: [PATCH] fix(ngAnimate): ignore children without animation data when closing them During parent structural animations, ongoing animations on child elements are closed. These child elements are identified by their data-ng-animate attribute. If an element is the clone of an animating element, it might have this attribute, but no animation runner associated with it, so we need to ignore it. Fixes #11992 Closes #13424 --- src/ngAnimate/animateQueue.js | 16 +++---- test/ngAnimate/animateSpec.js | 85 ++++++++++++++++++++++------------- 2 files changed, 62 insertions(+), 39 deletions(-) diff --git a/src/ngAnimate/animateQueue.js b/src/ngAnimate/animateQueue.js index 0d22adc35c40..0fdadd508609 100644 --- a/src/ngAnimate/animateQueue.js +++ b/src/ngAnimate/animateQueue.js @@ -512,15 +512,15 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) { forEach(children, function(child) { var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME)); var animationDetails = activeAnimationsLookup.get(child); - switch (state) { - case RUNNING_STATE: - animationDetails.runner.end(); - /* falls through */ - case PRE_DIGEST_STATE: - if (animationDetails) { + if (animationDetails) { + switch (state) { + case RUNNING_STATE: + animationDetails.runner.end(); + /* falls through */ + case PRE_DIGEST_STATE: activeAnimationsLookup.remove(child); - } - break; + break; + } } }); } diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js index 429b0679a527..cdc91a59312d 100644 --- a/test/ngAnimate/animateSpec.js +++ b/test/ngAnimate/animateSpec.js @@ -834,47 +834,70 @@ describe("animations", function() { expect(capturedAnimation[0]).toBe(element); })); - it('should skip all pre-digest queued child animations when a parent structural animation is triggered', - inject(function($rootScope, $rootElement, $animate) { + describe('when a parent structural animation is triggered:', function() { - parent.append(element); + it('should skip all pre-digest queued child animations', + inject(function($rootScope, $rootElement, $animate) { - $animate.addClass(element, 'rumlow'); - $animate.move(parent, null, parent2); + parent.append(element); - expect(capturedAnimation).toBeFalsy(); - expect(capturedAnimationHistory.length).toBe(0); - $rootScope.$digest(); + $animate.addClass(element, 'rumlow'); + $animate.move(parent, null, parent2); - expect(capturedAnimation[0]).toBe(parent); - expect(capturedAnimationHistory.length).toBe(1); - })); + expect(capturedAnimation).toBeFalsy(); + expect(capturedAnimationHistory.length).toBe(0); + $rootScope.$digest(); - it('should end all ongoing post-digest child animations when a parent structural animation is triggered', - inject(function($rootScope, $rootElement, $animate) { + expect(capturedAnimation[0]).toBe(parent); + expect(capturedAnimationHistory.length).toBe(1); + })); - parent.append(element); + it('should end all ongoing post-digest child animations', + inject(function($rootScope, $rootElement, $animate) { - $animate.addClass(element, 'rumlow'); - var isCancelled = false; - overriddenAnimationRunner = extend(defaultFakeAnimationRunner, { - end: function() { - isCancelled = true; - } - }); + parent.append(element); - $rootScope.$digest(); - expect(capturedAnimation[0]).toBe(element); - expect(isCancelled).toBe(false); + $animate.addClass(element, 'rumlow'); + var isCancelled = false; + overriddenAnimationRunner = extend(defaultFakeAnimationRunner, { + end: function() { + isCancelled = true; + } + }); - // restore the default - overriddenAnimationRunner = defaultFakeAnimationRunner; - $animate.move(parent, null, parent2); - $rootScope.$digest(); - expect(capturedAnimation[0]).toBe(parent); + $rootScope.$digest(); + expect(capturedAnimation[0]).toBe(element); + expect(isCancelled).toBe(false); - expect(isCancelled).toBe(true); - })); + // restore the default + overriddenAnimationRunner = defaultFakeAnimationRunner; + $animate.move(parent, null, parent2); + $rootScope.$digest(); + expect(capturedAnimation[0]).toBe(parent); + + expect(isCancelled).toBe(true); + })); + + it('should ignore children that have animation data-attributes but no animation data', + inject(function($rootScope, $rootElement, $animate) { + + parent.append(element); + + $animate.addClass(element, 'rumlow'); + + $rootScope.$digest(); + expect(capturedAnimation[0]).toBe(element); + + // If an element is cloned during an animation, the clone has the data-attributes indicating + // an animation + var clone = element.clone(); + parent.append(clone); + + $animate.move(parent, null, parent2); + $rootScope.$digest(); + expect(capturedAnimation[0]).toBe(parent); + })); + }); it('should not end any child animations if a parent class-based animation is issued', inject(function($rootScope, $rootElement, $animate) {