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

Commit

Permalink
feat($animate): provide support for DOM callbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
matsko committed Jan 14, 2014
1 parent 4ae3184 commit dde1b29
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 3 deletions.
41 changes: 38 additions & 3 deletions src/ngAnimate/animate.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [],
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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
Expand All @@ -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);
}
}
Expand Down
62 changes: 62 additions & 0 deletions test/ngAnimate/animateSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit dde1b29

Please sign in to comment.