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

Commit 2623de1

Browse files
committed
fix($animate): ensure animations work properly when the $rootElement is being animated
Closes #4397 Closes #4231
1 parent f5289fe commit 2623de1

File tree

2 files changed

+129
-12
lines changed

2 files changed

+129
-12
lines changed

src/ngAnimate/animate.js

+41-12
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,9 @@ angular.module('ngAnimate', ['ng'])
205205
var ELEMENT_NODE = 1;
206206
var NG_ANIMATE_STATE = '$$ngAnimateState';
207207
var NG_ANIMATE_CLASS_NAME = 'ng-animate';
208-
var rootAnimateState = {running:true};
209-
$provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope',
210-
function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope) {
208+
var rootAnimateState = {disabled:true};
209+
$provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope', '$document',
210+
function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope, $document) {
211211

212212
$rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
213213

@@ -466,18 +466,17 @@ angular.module('ngAnimate', ['ng'])
466466
}
467467
else {
468468
var data = element.data(NG_ANIMATE_STATE) || {};
469-
data.structural = true;
470-
data.running = true;
469+
data.disabled = true;
471470
element.data(NG_ANIMATE_STATE, data);
472471
}
473472
break;
474473

475474
case 1:
476-
rootAnimateState.running = !value;
475+
rootAnimateState.disabled = !value;
477476
break;
478477

479478
default:
480-
value = !rootAnimateState.running;
479+
value = !rootAnimateState.disabled;
481480
break;
482481
}
483482
return !!value;
@@ -498,7 +497,6 @@ angular.module('ngAnimate', ['ng'])
498497
parent = after ? after.parent() : element.parent();
499498
}
500499

501-
var disabledAnimation = { running : true };
502500
var matches = lookup(animationLookup);
503501
var isClassBased = event == 'addClass' || event == 'removeClass';
504502
var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
@@ -507,7 +505,7 @@ angular.module('ngAnimate', ['ng'])
507505
//the element is not currently attached to the document body or then completely close
508506
//the animation if any matching animations are not found at all.
509507
//NOTE: IE8 + IE9 should close properly (run done()) in case a NO animation is not found.
510-
if ((parent.inheritedData(NG_ANIMATE_STATE) || disabledAnimation).running || matches.length == 0) {
508+
if (animationsDisabled(element, parent) || matches.length === 0) {
511509
done();
512510
return;
513511
}
@@ -528,7 +526,7 @@ angular.module('ngAnimate', ['ng'])
528526

529527
//this would mean that an animation was not allowed so let the existing
530528
//animation do it's thing and close this one early
531-
if(animations.length == 0) {
529+
if(animations.length === 0) {
532530
onComplete && onComplete();
533531
return;
534532
}
@@ -622,8 +620,39 @@ angular.module('ngAnimate', ['ng'])
622620
}
623621

624622
function cleanup(element) {
625-
element.removeClass(NG_ANIMATE_CLASS_NAME);
626-
element.removeData(NG_ANIMATE_STATE);
623+
if(element[0] == $rootElement[0]) {
624+
if(!rootAnimateState.disabled) {
625+
rootAnimateState.running = false;
626+
rootAnimateState.structural = false;
627+
}
628+
}
629+
else {
630+
element.removeClass(NG_ANIMATE_CLASS_NAME);
631+
element.removeData(NG_ANIMATE_STATE);
632+
}
633+
}
634+
635+
function animationsDisabled(element, parent) {
636+
if(element == $rootElement) {
637+
return rootAnimateState.disabled || rootAnimateState.running;
638+
}
639+
640+
var validState;
641+
do {
642+
//the element did not reach the root element which means that it
643+
//is not apart of the DOM. Therefore there is no reason to do
644+
//any animations on it
645+
if(parent.length === 0 || parent[0] == $document[0]) return true;
646+
647+
var state = parent.data(NG_ANIMATE_STATE);
648+
if(state && (state.disabled != null || state.running != null)) {
649+
validState = state;
650+
break;
651+
}
652+
}
653+
while(parent = parent.parent());
654+
655+
return validState ? (validState.disabled || validState.running) : true;
627656
}
628657
}]);
629658

test/ngAnimate/animateSpec.js

+88
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,70 @@ describe("ngAnimate", function() {
5656
expect($animate.enabled(1)).toBe(true);
5757
expect($animate.enabled()).toBe(true);
5858
});
59+
60+
it('should place a hard disable on all child animations', function() {
61+
var count = 0;
62+
module(function($animateProvider) {
63+
$animateProvider.register('.animated', function() {
64+
return {
65+
addClass : function(element, className, done) {
66+
count++;
67+
done();
68+
}
69+
}
70+
});
71+
});
72+
inject(function($compile, $rootScope, $animate, $sniffer, $rootElement, $timeout) {
73+
$animate.enabled(true);
74+
75+
var elm1 = $compile('<div class="animated"></div>')($rootScope);
76+
var elm2 = $compile('<div class="animated"></div>')($rootScope);
77+
$rootElement.append(elm1);
78+
angular.element(document.body).append($rootElement);
79+
80+
$animate.addClass(elm1, 'klass');
81+
expect(count).toBe(1);
82+
83+
$animate.enabled(false);
84+
85+
$animate.addClass(elm1, 'klass2');
86+
expect(count).toBe(1);
87+
88+
$animate.enabled(true);
89+
90+
elm1.append(elm2);
91+
92+
$animate.addClass(elm2, 'klass');
93+
expect(count).toBe(2);
94+
95+
$animate.enabled(false, elm1);
96+
97+
$animate.addClass(elm2, 'klass2');
98+
expect(count).toBe(2);
99+
});
100+
});
101+
102+
it('should skip animations if the element is attached to the $rootElement', function() {
103+
var count = 0;
104+
module(function($animateProvider) {
105+
$animateProvider.register('.animated', function() {
106+
return {
107+
addClass : function(element, className, done) {
108+
count++;
109+
done();
110+
}
111+
}
112+
});
113+
});
114+
inject(function($compile, $rootScope, $animate, $sniffer, $rootElement, $timeout) {
115+
$animate.enabled(true);
116+
117+
var elm1 = $compile('<div class="animated"></div>')($rootScope);
118+
119+
$animate.addClass(elm1, 'klass2');
120+
expect(count).toBe(0);
121+
});
122+
});
59123
});
60124

61125
describe("with polyfill", function() {
@@ -1956,4 +2020,28 @@ describe("ngAnimate", function() {
19562020
expect(element.hasClass('red-add')).toBe(false);
19572021
expect(element.hasClass('yellow-add')).toBe(true);
19582022
}));
2023+
2024+
it('should enable and disable animations properly on the root element', function() {
2025+
var count = 0;
2026+
module(function($animateProvider) {
2027+
$animateProvider.register('.animated', function() {
2028+
return {
2029+
addClass : function(element, className, done) {
2030+
count++;
2031+
done();
2032+
}
2033+
}
2034+
});
2035+
});
2036+
inject(function($compile, $rootScope, $animate, $sniffer, $rootElement, $timeout) {
2037+
2038+
$rootElement.addClass('animated');
2039+
$animate.addClass($rootElement, 'green');
2040+
expect(count).toBe(1);
2041+
2042+
$animate.addClass($rootElement, 'red');
2043+
expect(count).toBe(2);
2044+
});
2045+
});
2046+
19592047
});

0 commit comments

Comments
 (0)