From 3105b2c26a71594c4e7904efc18f4b2e9da25b1b Mon Sep 17 00:00:00 2001 From: Martin Staffa Date: Thu, 6 Sep 2018 09:47:12 +0200 Subject: [PATCH] fix(ngAnimate): remove prepare classes with multiple structural animations Closes #16681 Closes #16677 --- src/ngAnimate/animation.js | 5 +- test/ngAnimate/integrationSpec.js | 81 +++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/src/ngAnimate/animation.js b/src/ngAnimate/animation.js index 227009af3be2..239f38f03b2e 100644 --- a/src/ngAnimate/animation.js +++ b/src/ngAnimate/animation.js @@ -113,8 +113,6 @@ var $$AnimationProvider = ['$animateProvider', /** @this */ function($animatePro // TODO(matsko): document the signature in a better way return function(element, event, options) { - var node = getDomNode(element); - options = prepareAnimationOptions(options); var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0; @@ -186,8 +184,9 @@ var $$AnimationProvider = ['$animateProvider', /** @this */ function($animatePro forEach(groupedAnimations, function(animationEntry) { var element = animationEntry.from ? animationEntry.from.element : animationEntry.element; var extraClasses = options.addClass; + extraClasses = (extraClasses ? (extraClasses + ' ') : '') + NG_ANIMATE_CLASSNAME; - var cacheKey = $$animateCache.cacheKey(node, event, extraClasses, options.removeClass); + var cacheKey = $$animateCache.cacheKey(element[0], animationEntry.event, extraClasses, options.removeClass); toBeSortedAnimations.push({ element: element, diff --git a/test/ngAnimate/integrationSpec.js b/test/ngAnimate/integrationSpec.js index cc6359f715d7..63c9747bb673 100644 --- a/test/ngAnimate/integrationSpec.js +++ b/test/ngAnimate/integrationSpec.js @@ -25,6 +25,7 @@ describe('ngAnimate integration tests', function() { ss.destroy(); }); + it('should cancel a running and started removeClass animation when a follow-up addClass animation adds the same class', inject(function($animate, $rootScope, $$rAF, $document, $rootElement) { @@ -362,6 +363,7 @@ describe('ngAnimate integration tests', function() { }); }); + it('should add the preparation class for an enter animation before a parent class-based animation is applied', function() { module('ngAnimateMock'); inject(function($animate, $compile, $rootScope, $rootElement, $document) { @@ -397,6 +399,7 @@ describe('ngAnimate integration tests', function() { }); }); + it('should avoid adding the ng-enter-prepare method to a parent structural animation that contains child animations', function() { module('ngAnimateMock'); inject(function($animate, $compile, $rootScope, $rootElement, $document, $$rAF) { @@ -468,6 +471,84 @@ describe('ngAnimate integration tests', function() { }); }); + + it('should remove the prepare classes when different structural animations happen in the same digest', function() { + module('ngAnimateMock'); + inject(function($animate, $compile, $rootScope, $rootElement, $document, $$animateCache) { + element = jqLite( + // Class animation on parent element is neeeded so the child elements get the prepare class + '
' + + '
' + + '
' + + '
' + ); + + $rootElement.append(element); + jqLite($document[0].body).append($rootElement); + + $compile(element)($rootScope); + $rootScope.cond = false; + $rootScope.$digest(); + + $rootScope.cond = true; + $rootScope.$digest(); + + var parent = element; + var truthySwitch = jqLite(parent[0].querySelector('#truthy')); + var defaultSwitch = jqLite(parent[0].querySelector('#default')); + + expect(parent).not.toHaveClass('blue'); + expect(parent).toHaveClass('blue-add'); + expect(truthySwitch).toHaveClass('ng-enter-prepare'); + expect(defaultSwitch).toHaveClass('ng-leave-prepare'); + + $animate.flush(); + + expect(parent).toHaveClass('blue'); + expect(parent).not.toHaveClass('blue-add'); + expect(truthySwitch).not.toHaveClass('ng-enter-prepare'); + expect(defaultSwitch).not.toHaveClass('ng-leave-prepare'); + }); + }); + + it('should respect the element node for caching when animations with the same type happen in the same digest', function() { + module('ngAnimateMock'); + inject(function($animate, $compile, $rootScope, $rootElement, $document, $$animateCache) { + ss.addRule('.animate.ng-enter', 'transition:2s linear all;'); + + element = jqLite( + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + ); + + $rootElement.append(element); + jqLite($document[0].body).append($rootElement); + + $compile(element)($rootScope); + $rootScope.cond = true; + $rootScope.$digest(); + + var parent = element; + var noanimate = jqLite(parent[0].querySelector('#noanimate')); + var animate = jqLite(parent[0].querySelector('#animate')); + + expect(noanimate).not.toHaveClass('ng-enter'); + expect(animate).toHaveClass('ng-enter'); + + $animate.closeAndFlush(); + + expect(noanimate).not.toHaveClass('ng-enter'); + expect(animate).not.toHaveClass('ng-enter'); + }); + }); + + it('should pack level elements into their own RAF flush', function() { module('ngAnimateMock'); inject(function($animate, $compile, $rootScope, $rootElement, $document) {