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

Commit 38915e2

Browse files
committed
fix($animate): abort class-based animations if the element is removed during digest
Prior to this fix, if the element is removed before the digest kicks off then it leads to an error when a class based animation is run. This fix ensures that the animation will not run at all if the element does not have a parent element. Closes #8796
1 parent cbdaabf commit 38915e2

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

src/ngAnimate/animate.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1015,14 +1015,23 @@ angular.module('ngAnimate', ['ng'])
10151015
}
10161016

10171017
return cache.promise = runAnimationPostDigest(function(done) {
1018+
var parentElement;
1019+
if (!isMatchingElement(element, $rootElement)) { //$rootElement's parent is not inside of the app
1020+
parentElement = element.parent();
1021+
if (!parentElement || parentElement.length === 0) {
1022+
done();
1023+
return;
1024+
}
1025+
}
1026+
10181027
var cache = element.data(STORAGE_KEY);
10191028
element.removeData(STORAGE_KEY);
10201029

10211030
var state = element.data(NG_ANIMATE_STATE) || {};
10221031
var classes = resolveElementClasses(element, cache, state.active);
10231032
return !classes
10241033
? done()
1025-
: performAnimation('setClass', classes, element, null, null, function() {
1034+
: performAnimation('setClass', classes, element, parentElement, null, function() {
10261035
$delegate.setClass(element, classes[0], classes[1]);
10271036
}, done);
10281037
});

test/ngAnimate/animateSpec.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3415,6 +3415,54 @@ describe("ngAnimate", function() {
34153415
});
34163416
});
34173417

3418+
it('should skip class-based animations if the element is removed before the digest occurs', function() {
3419+
var spy = jasmine.createSpy();
3420+
module(function($animateProvider) {
3421+
$animateProvider.register('.animated', function() {
3422+
return {
3423+
beforeAddClass : spy,
3424+
beforeRemoveClass : spy,
3425+
beforeSetClass : spy
3426+
};
3427+
});
3428+
});
3429+
inject(function($rootScope, $animate, $compile, $rootElement, $document) {
3430+
$animate.enabled(true);
3431+
3432+
var one = $compile('<div class="animated"></div>')($rootScope);
3433+
var two = $compile('<div class="animated"></div>')($rootScope);
3434+
var three = $compile('<div class="animated three"></div>')($rootScope);
3435+
3436+
$rootElement.append(one);
3437+
$rootElement.append(two);
3438+
angular.element($document[0].body).append($rootElement);
3439+
3440+
$animate.addClass(one, 'active-class');
3441+
one.remove();
3442+
3443+
$rootScope.$digest();
3444+
expect(spy).not.toHaveBeenCalled();
3445+
3446+
$animate.addClass(two, 'active-class');
3447+
3448+
$rootScope.$digest();
3449+
expect(spy).toHaveBeenCalled();
3450+
3451+
spy.reset();
3452+
$animate.removeClass(two, 'active-class');
3453+
two.remove();
3454+
3455+
$rootScope.$digest();
3456+
expect(spy).not.toHaveBeenCalled();
3457+
3458+
$animate.setClass(three, 'active-class', 'three');
3459+
three.remove();
3460+
3461+
$rootScope.$digest();
3462+
expect(spy).not.toHaveBeenCalled();
3463+
});
3464+
});
3465+
34183466
it('should call class-based animation callbacks in the correct order when animations are skipped', function() {
34193467
var continueAnimation;
34203468
module(function($animateProvider) {

0 commit comments

Comments
 (0)