From a238d4316c2984111dcfd0f31711cc985698d119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Thu, 2 Oct 2014 12:41:36 +0200 Subject: [PATCH] fix($animate): clear the GCS cache even when no animation is detected $animate will cache subsequent calls to GCS in the event that the element with the same CSS classes and the same parentNode is being animated. Once the animation is started then $animate waits for one rAF before flushing the GCS lookup cache. Prior to this fix, if GCS was unable to detect any transitions or keyframes on the element then it would simply close the animation, but it would not trigger the rAF code to flush the cache. This issue caused a bug which made it difficult to detect why certain animations are not allowed to fire if the element didn't contain any CSS-based animations beforehand. Closes #8813 --- src/ngAnimate/animate.js | 14 +++++++++ test/ngAnimate/animateSpec.js | 58 +++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/src/ngAnimate/animate.js b/src/ngAnimate/animate.js index e66fcd6e1cfc..f78adcdc372f 100644 --- a/src/ngAnimate/animate.js +++ b/src/ngAnimate/animate.js @@ -1419,6 +1419,16 @@ angular.module('ngAnimate', ['ng']) var parentCounter = 0; var animationReflowQueue = []; var cancelAnimationReflow; + function clearCacheAfterReflow() { + if (!cancelAnimationReflow) { + cancelAnimationReflow = $$animateReflow(function() { + animationReflowQueue = []; + cancelAnimationReflow = null; + lookupCache = {}; + }); + } + } + function afterReflow(element, callback) { if (cancelAnimationReflow) { cancelAnimationReflow(); @@ -1764,6 +1774,7 @@ angular.module('ngAnimate', ['ng']) //to perform at all var preReflowCancellation = animateBefore(animationEvent, element, className); if (!preReflowCancellation) { + clearCacheAfterReflow(); animationComplete(); return; } @@ -1820,6 +1831,7 @@ angular.module('ngAnimate', ['ng']) afterReflow(element, animationCompleted); return cancellationMethod; } + clearCacheAfterReflow(); animationCompleted(); }, @@ -1829,6 +1841,7 @@ angular.module('ngAnimate', ['ng']) afterReflow(element, animationCompleted); return cancellationMethod; } + clearCacheAfterReflow(); animationCompleted(); }, @@ -1838,6 +1851,7 @@ angular.module('ngAnimate', ['ng']) afterReflow(element, animationCompleted); return cancellationMethod; } + clearCacheAfterReflow(); animationCompleted(); }, diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js index a571464e2142..b738f1e1ef61 100644 --- a/test/ngAnimate/animateSpec.js +++ b/test/ngAnimate/animateSpec.js @@ -3757,6 +3757,64 @@ describe("ngAnimate", function() { expect(inner.hasClass('on-add-active')).toBe(false); })); + it("should reset the getComputedStyle lookup cache even when no animation is found", + inject(function($compile, $rootScope, $animate, $sniffer, $document) { + + if (!$sniffer.transitions) return; + + $animate.enabled(); + + var html = '
' + + '
On or Off
' + + '
'; + + ss.addRule('.activated .toggle', '-webkit-transition:1s linear all;' + + 'transition:1s linear all;'); + + var child, element = $compile(html)($rootScope); + + $rootElement.append(element); + jqLite($document[0].body).append($rootElement); + + $rootScope.onOff = true; + $rootScope.$digest(); + + child = element.find('div'); + expect(child).not.toHaveClass('ng-enter'); + expect(child.parent()[0]).toEqual(element[0]); + $animate.triggerReflow(); + + $rootScope.onOff = false; + $rootScope.$digest(); + + child = element.find('div'); + expect(child.parent().length).toBe(0); + $animate.triggerReflow(); + + element.addClass('activated'); + $rootScope.$digest(); + $animate.triggerReflow(); + + $rootScope.onOff = true; + $rootScope.$digest(); + + child = element.find('div'); + expect(child).toHaveClass('ng-enter'); + $animate.triggerReflow(); + expect(child).toHaveClass('ng-enter-active'); + + browserTrigger(child, 'transitionend', + { timeStamp: Date.now() + 1000, elapsedTime: 2000 }); + + $animate.triggerCallbacks(); + + $rootScope.onOff = false; + $rootScope.$digest(); + + expect(child).toHaveClass('ng-leave'); + $animate.triggerReflow(); + expect(child).toHaveClass('ng-leave-active'); + })); it("should cancel and perform the dom operation only after the reflow has run", inject(function($compile, $rootScope, $animate, $sniffer) {