Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit dde1b29

Browse files
committed
feat($animate): provide support for DOM callbacks
1 parent 4ae3184 commit dde1b29

File tree

2 files changed

+100
-3
lines changed

2 files changed

+100
-3
lines changed

src/ngAnimate/animate.js

+38-3
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,10 @@ angular.module('ngAnimate', ['ng'])
317317
return classNameFilter.test(className);
318318
};
319319

320+
function async(fn) {
321+
return $timeout(fn, 0, false);
322+
}
323+
320324
function lookup(name) {
321325
if (name) {
322326
var matches = [],
@@ -608,6 +612,8 @@ angular.module('ngAnimate', ['ng'])
608612
//best to catch this early on to prevent any animation operations from occurring
609613
if(!node || !isAnimatableClassName(classes)) {
610614
fireDOMOperation();
615+
fireBeforeCallbackAsync();
616+
fireAfterCallbackAsync();
611617
closeAnimation();
612618
return;
613619
}
@@ -627,6 +633,8 @@ angular.module('ngAnimate', ['ng'])
627633
//NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case a NO animation is not found.
628634
if (animationsDisabled(element, parentElement) || matches.length === 0) {
629635
fireDOMOperation();
636+
fireBeforeCallbackAsync();
637+
fireAfterCallbackAsync();
630638
closeAnimation();
631639
return;
632640
}
@@ -665,6 +673,8 @@ angular.module('ngAnimate', ['ng'])
665673
//animation do it's thing and close this one early
666674
if(animations.length === 0) {
667675
fireDOMOperation();
676+
fireBeforeCallbackAsync();
677+
fireAfterCallbackAsync();
668678
fireDoneCallbackAsync();
669679
return;
670680
}
@@ -718,6 +728,8 @@ angular.module('ngAnimate', ['ng'])
718728
if((animationEvent == 'addClass' && futureClassName.indexOf(classNameToken) >= 0) ||
719729
(animationEvent == 'removeClass' && futureClassName.indexOf(classNameToken) == -1)) {
720730
fireDOMOperation();
731+
fireBeforeCallbackAsync();
732+
fireAfterCallbackAsync();
721733
fireDoneCallbackAsync();
722734
return;
723735
}
@@ -758,6 +770,10 @@ angular.module('ngAnimate', ['ng'])
758770
}
759771

760772
function invokeRegisteredAnimationFns(animations, phase, allAnimationFnsComplete) {
773+
phase == 'after' ?
774+
fireAfterCallbackAsync() :
775+
fireBeforeCallbackAsync();
776+
761777
var endFnName = phase + 'End';
762778
forEach(animations, function(animation, index) {
763779
var animationPhaseCompleted = function() {
@@ -794,8 +810,27 @@ angular.module('ngAnimate', ['ng'])
794810
}
795811
}
796812

813+
function fireDOMCallback(animationPhase) {
814+
element.triggerHandler('$animate:' + animationPhase, {
815+
event : animationEvent,
816+
className : className
817+
});
818+
}
819+
820+
function fireBeforeCallbackAsync() {
821+
async(function() {
822+
fireDOMCallback('before');
823+
});
824+
}
825+
826+
function fireAfterCallbackAsync() {
827+
async(function() {
828+
fireDOMCallback('after');
829+
});
830+
}
831+
797832
function fireDoneCallbackAsync() {
798-
doneCallback && $timeout(doneCallback, 0, false);
833+
doneCallback && async(doneCallback);
799834
}
800835

801836
//it is less complicated to use a flag than managing and cancelling
@@ -819,9 +854,9 @@ angular.module('ngAnimate', ['ng'])
819854
if(isClassBased) {
820855
cleanup(element);
821856
} else {
822-
data.closeAnimationTimeout = $timeout(function() {
857+
data.closeAnimationTimeout = async(function() {
823858
cleanup(element);
824-
}, 0, false);
859+
});
825860
element.data(NG_ANIMATE_STATE, data);
826861
}
827862
}

test/ngAnimate/animateSpec.js

+62
Original file line numberDiff line numberDiff line change
@@ -1496,6 +1496,68 @@ describe("ngAnimate", function() {
14961496
expect(signature).toBe('AB');
14971497
}));
14981498

1499+
it('should fire DOM callbacks on the element being animated',
1500+
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
1501+
1502+
if(!$sniffer.transitions) return;
1503+
1504+
$animate.enabled(true);
1505+
1506+
ss.addRule('.klass-add', '-webkit-transition:1s linear all;' +
1507+
'transition:1s linear all;');
1508+
1509+
var element = jqLite('<div></div>');
1510+
$rootElement.append(element);
1511+
body.append($rootElement);
1512+
1513+
var steps = [];
1514+
element.on('$animate:before', function(e, data) {
1515+
steps.push(['before', data.className, data.event]);
1516+
});
1517+
1518+
element.on('$animate:after', function(e, data) {
1519+
steps.push(['after', data.className, data.event]);
1520+
});
1521+
1522+
$animate.addClass(element, 'klass');
1523+
1524+
$timeout.flush(1);
1525+
1526+
expect(steps.pop()).toEqual(['before', 'klass', 'addClass']);
1527+
1528+
$animate.triggerReflow();
1529+
$timeout.flush(1);
1530+
1531+
expect(steps.pop()).toEqual(['after', 'klass', 'addClass']);
1532+
}));
1533+
1534+
it('should fire the DOM callbacks even if no animation is rendered',
1535+
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
1536+
1537+
$animate.enabled(true);
1538+
1539+
var parent = jqLite('<div></div>');
1540+
var element = jqLite('<div></div>');
1541+
$rootElement.append(parent);
1542+
body.append($rootElement);
1543+
1544+
var steps = [];
1545+
element.on('$animate:before', function(e, data) {
1546+
steps.push(['before', data.className, data.event]);
1547+
});
1548+
1549+
element.on('$animate:after', function(e, data) {
1550+
steps.push(['after', data.className, data.event]);
1551+
});
1552+
1553+
$animate.enter(element, parent);
1554+
$rootScope.$digest();
1555+
1556+
$timeout.flush(1);
1557+
1558+
expect(steps.shift()).toEqual(['before', 'ng-enter', 'enter']);
1559+
expect(steps.shift()).toEqual(['after', 'ng-enter', 'enter']);
1560+
}));
14991561

15001562
it("should fire a done callback when provided with no animation",
15011563
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {

0 commit comments

Comments
 (0)