From fd21c7502f0a25364a810c26ebeecb678e5783c5 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Thu, 2 May 2013 10:47:30 -0400 Subject: [PATCH] fix(ngAnamite): eval ng-animate expression on each animation --- src/ng/animator.js | 102 +++++++++++++++++++--------------------- test/ng/animatorSpec.js | 25 ++++++++-- 2 files changed, 71 insertions(+), 56 deletions(-) diff --git a/src/ng/animator.js b/src/ng/animator.js index d79822f7cd2..f2143f59dc1 100644 --- a/src/ng/animator.js +++ b/src/ng/animator.js @@ -147,8 +147,6 @@ var $AnimatorProvider = function() { * @return {object} the animator object which contains the enter, leave, move, show, hide and animate methods. */ var AnimatorService = function(scope, attrs) { - var ngAnimateAttr = attrs.ngAnimate; - var ngAnimateValue = ngAnimateAttr && scope.$eval(ngAnimateAttr); var animator = {}; /** @@ -223,24 +221,22 @@ var $AnimatorProvider = function() { return animator; function animateActionFactory(type, beforeFn, afterFn) { - var className = ngAnimateAttr - ? isObject(ngAnimateValue) ? ngAnimateValue[type] : ngAnimateValue + '-' + type - : ''; - var animationPolyfill = $animation(className); - - var polyfillSetup = animationPolyfill && animationPolyfill.setup; - var polyfillStart = animationPolyfill && animationPolyfill.start; - - if (!className) { - return function(element, parent, after) { + return function(element, parent, after) { + var ngAnimateValue = scope.$eval(attrs.ngAnimate); + var className = ngAnimateValue + ? isObject(ngAnimateValue) ? ngAnimateValue[type] : ngAnimateValue + '-' + type + : ''; + var animationPolyfill = $animation(className); + var polyfillSetup = animationPolyfill && animationPolyfill.setup; + var polyfillStart = animationPolyfill && animationPolyfill.start; + + if (!className) { beforeFn(element, parent, after); afterFn(element, parent, after); - } - } else { - var setupClass = className + '-setup'; - var startClass = className + '-start'; - - return function(element, parent, after) { + } else { + var setupClass = className + '-setup'; + var startClass = className + '-start'; + if (!parent) { parent = after ? after.parent() : element.parent(); } @@ -255,48 +251,48 @@ var $AnimatorProvider = function() { element.addClass(setupClass); beforeFn(element, parent, after); if (element.length == 0) return done(); - + var memento = (polyfillSetup || noop)(element); - + // $window.setTimeout(beginAnimation, 0); this was causing the element not to animate // keep at 1 for animation dom rerender $window.setTimeout(beginAnimation, 1); - - function beginAnimation() { - element.addClass(startClass); - if (polyfillStart) { - polyfillStart(element, done, memento); - } else if (isFunction($window.getComputedStyle)) { - var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition'; - var w3cTransitionProp = 'transition'; //one day all browsers will have this - - var durationKey = 'Duration'; - var duration = 0; + }; - //we want all the styles defined before and after - forEach(element, function(element) { - if (element.nodeType == 1) { - var globalStyles = $window.getComputedStyle(element) || {}; - duration = Math.max( - parseFloat(globalStyles[w3cTransitionProp + durationKey]) || - parseFloat(globalStyles[vendorTransitionProp + durationKey]) || - 0, - duration); - } - }); - $window.setTimeout(done, duration * 1000); - } else { - done(); - } - } - - function done() { - afterFn(element, parent, after); - element.removeClass(setupClass); - element.removeClass(startClass); - element.removeData(NG_ANIMATE_CONTROLLER); + function beginAnimation() { + element.addClass(startClass); + if (polyfillStart) { + polyfillStart(element, done, memento); + } else if (isFunction($window.getComputedStyle)) { + var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition'; + var w3cTransitionProp = 'transition'; //one day all browsers will have this + + var durationKey = 'Duration'; + var duration = 0; + + //we want all the styles defined before and after + forEach(element, function(element) { + if (element.nodeType == 1) { + var globalStyles = $window.getComputedStyle(element) || {}; + duration = Math.max( + parseFloat(globalStyles[w3cTransitionProp + durationKey]) || + parseFloat(globalStyles[vendorTransitionProp + durationKey]) || + 0, + duration); + } + }); + $window.setTimeout(done, duration * 1000); + } else { + done(); } } + + function done() { + afterFn(element, parent, after); + element.removeClass(setupClass); + element.removeClass(startClass); + element.removeData(NG_ANIMATE_CONTROLLER); + } } } diff --git a/test/ng/animatorSpec.js b/test/ng/animatorSpec.js index 1b393e9114f..daab9721ea4 100644 --- a/test/ng/animatorSpec.js +++ b/test/ng/animatorSpec.js @@ -336,10 +336,29 @@ describe("$animator", function() { })); }); - it("should throw an error when an invalid ng-animate syntax is provided", inject(function($compile, $rootScope) { + describe('anmation evaluation', function () { + it('should re-evaluate the animation expression on each animation', inject(function($animator, $rootScope) { + var parent = jqLite('
'); + var element = parent.find('span'); + + $rootScope.animationFn = function () { throw new Error('too early'); }; + var animate = $animator($rootScope, { ngAnimate: 'animationFn()' }); + var log = ''; + + $rootScope.animationFn = function () { log = 'abc' }; + animate.enter(element, parent); + expect(log).toEqual('abc'); + + $rootScope.animationFn = function () { log = 'xyz' }; + animate.enter(element, parent); + expect(log).toEqual('xyz'); + })); + }); + + it("should throw an error when an invalid ng-animate syntax is provided", inject(function($animator, $rootScope) { expect(function() { - element = $compile('
')($rootScope); - $rootScope.$digest(); + var animate = $animator($rootScope, { ngAnimate: ':' }); + animate.enter(); }).toThrow("Syntax Error: Token ':' not a primary expression at column 1 of the expression [:] starting at [:]."); })); });