diff options
| author | Matias Niemelä | 2014-01-13 21:51:08 -0500 | 
|---|---|---|
| committer | Matias Niemelä | 2014-01-14 13:21:28 -0500 | 
| commit | dde1b2949727c297e214c99960141bfad438d7a4 (patch) | |
| tree | a433c7813bacdf98fa04f075becec0604b0b155c | |
| parent | 4ae3184c5915aac9aa00889aa2153c8e84c14966 (diff) | |
| download | angular.js-dde1b2949727c297e214c99960141bfad438d7a4.tar.bz2 | |
feat($animate): provide support for DOM callbacks
| -rw-r--r-- | src/ngAnimate/animate.js | 41 | ||||
| -rw-r--r-- | test/ngAnimate/animateSpec.js | 62 | 
2 files changed, 100 insertions, 3 deletions
| diff --git a/src/ngAnimate/animate.js b/src/ngAnimate/animate.js index 0417f18e..4f243220 100644 --- a/src/ngAnimate/animate.js +++ b/src/ngAnimate/animate.js @@ -317,6 +317,10 @@ angular.module('ngAnimate', ['ng'])                  return classNameFilter.test(className);                }; +      function async(fn) { +        return $timeout(fn, 0, false); +      } +        function lookup(name) {          if (name) {            var matches = [], @@ -608,6 +612,8 @@ angular.module('ngAnimate', ['ng'])          //best to catch this early on to prevent any animation operations from occurring          if(!node || !isAnimatableClassName(classes)) {            fireDOMOperation(); +          fireBeforeCallbackAsync(); +          fireAfterCallbackAsync();            closeAnimation();            return;          } @@ -627,6 +633,8 @@ angular.module('ngAnimate', ['ng'])          //NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case a NO animation is not found.          if (animationsDisabled(element, parentElement) || matches.length === 0) {            fireDOMOperation(); +          fireBeforeCallbackAsync(); +          fireAfterCallbackAsync();            closeAnimation();            return;          } @@ -665,6 +673,8 @@ angular.module('ngAnimate', ['ng'])          //animation do it's thing and close this one early          if(animations.length === 0) {            fireDOMOperation(); +          fireBeforeCallbackAsync(); +          fireAfterCallbackAsync();            fireDoneCallbackAsync();            return;          } @@ -718,6 +728,8 @@ angular.module('ngAnimate', ['ng'])          if((animationEvent == 'addClass'    && futureClassName.indexOf(classNameToken) >= 0) ||             (animationEvent == 'removeClass' && futureClassName.indexOf(classNameToken) == -1)) {            fireDOMOperation(); +          fireBeforeCallbackAsync(); +          fireAfterCallbackAsync();            fireDoneCallbackAsync();            return;          } @@ -758,6 +770,10 @@ angular.module('ngAnimate', ['ng'])          }          function invokeRegisteredAnimationFns(animations, phase, allAnimationFnsComplete) { +          phase == 'after' ? +            fireAfterCallbackAsync() : +            fireBeforeCallbackAsync(); +            var endFnName = phase + 'End';            forEach(animations, function(animation, index) {              var animationPhaseCompleted = function() { @@ -794,8 +810,27 @@ angular.module('ngAnimate', ['ng'])            }          } +        function fireDOMCallback(animationPhase) { +          element.triggerHandler('$animate:' + animationPhase, { +            event : animationEvent, +            className : className +          }); +        } + +        function fireBeforeCallbackAsync() { +          async(function() { +            fireDOMCallback('before'); +          }); +        } + +        function fireAfterCallbackAsync() { +          async(function() { +            fireDOMCallback('after'); +          }); +        } +          function fireDoneCallbackAsync() { -          doneCallback && $timeout(doneCallback, 0, false); +          doneCallback && async(doneCallback);          }          //it is less complicated to use a flag than managing and cancelling @@ -819,9 +854,9 @@ angular.module('ngAnimate', ['ng'])                if(isClassBased) {                  cleanup(element);                } else { -                data.closeAnimationTimeout = $timeout(function() { +                data.closeAnimationTimeout = async(function() {                    cleanup(element); -                }, 0, false); +                });                  element.data(NG_ANIMATE_STATE, data);                }              } diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js index 1477bca0..6d9367bd 100644 --- a/test/ngAnimate/animateSpec.js +++ b/test/ngAnimate/animateSpec.js @@ -1496,6 +1496,68 @@ describe("ngAnimate", function() {            expect(signature).toBe('AB');          })); +        it('should fire DOM callbacks on the element being animated', +          inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) { + +          if(!$sniffer.transitions) return; + +          $animate.enabled(true); + +          ss.addRule('.klass-add', '-webkit-transition:1s linear all;' + +                                           'transition:1s linear all;'); + +          var element = jqLite('<div></div>'); +          $rootElement.append(element); +          body.append($rootElement); + +          var steps = []; +          element.on('$animate:before', function(e, data) { +            steps.push(['before', data.className, data.event]); +          }); + +          element.on('$animate:after', function(e, data) { +            steps.push(['after', data.className, data.event]); +          }); + +          $animate.addClass(element, 'klass'); + +          $timeout.flush(1); + +          expect(steps.pop()).toEqual(['before', 'klass', 'addClass']); + +          $animate.triggerReflow(); +          $timeout.flush(1); + +          expect(steps.pop()).toEqual(['after', 'klass', 'addClass']); +        }));  + +        it('should fire the DOM callbacks even if no animation is rendered', +          inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) { + +          $animate.enabled(true); + +          var parent = jqLite('<div></div>'); +          var element = jqLite('<div></div>'); +          $rootElement.append(parent); +          body.append($rootElement); + +          var steps = []; +          element.on('$animate:before', function(e, data) { +            steps.push(['before', data.className, data.event]); +          }); + +          element.on('$animate:after', function(e, data) { +            steps.push(['after', data.className, data.event]); +          }); + +          $animate.enter(element, parent); +          $rootScope.$digest(); + +          $timeout.flush(1); + +          expect(steps.shift()).toEqual(['before', 'ng-enter', 'enter']); +          expect(steps.shift()).toEqual(['after',  'ng-enter', 'enter']); +        }));           it("should fire a done callback when provided with no animation",            inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) { | 
