aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/ngAnimate/animate.js41
-rw-r--r--test/ngAnimate/animateSpec.js62
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) {