mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-28 07:23:59 +00:00
fix($animate): ensure animations work properly when the $rootElement is being animated
Closes #4397 Closes #4231
This commit is contained in:
parent
f5289fe84f
commit
2623de1426
2 changed files with 129 additions and 12 deletions
|
|
@ -205,9 +205,9 @@ angular.module('ngAnimate', ['ng'])
|
||||||
var ELEMENT_NODE = 1;
|
var ELEMENT_NODE = 1;
|
||||||
var NG_ANIMATE_STATE = '$$ngAnimateState';
|
var NG_ANIMATE_STATE = '$$ngAnimateState';
|
||||||
var NG_ANIMATE_CLASS_NAME = 'ng-animate';
|
var NG_ANIMATE_CLASS_NAME = 'ng-animate';
|
||||||
var rootAnimateState = {running:true};
|
var rootAnimateState = {disabled:true};
|
||||||
$provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope',
|
$provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope', '$document',
|
||||||
function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope) {
|
function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope, $document) {
|
||||||
|
|
||||||
$rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
|
$rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
|
||||||
|
|
||||||
|
|
@ -466,18 +466,17 @@ angular.module('ngAnimate', ['ng'])
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var data = element.data(NG_ANIMATE_STATE) || {};
|
var data = element.data(NG_ANIMATE_STATE) || {};
|
||||||
data.structural = true;
|
data.disabled = true;
|
||||||
data.running = true;
|
|
||||||
element.data(NG_ANIMATE_STATE, data);
|
element.data(NG_ANIMATE_STATE, data);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
rootAnimateState.running = !value;
|
rootAnimateState.disabled = !value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
value = !rootAnimateState.running;
|
value = !rootAnimateState.disabled;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return !!value;
|
return !!value;
|
||||||
|
|
@ -498,7 +497,6 @@ angular.module('ngAnimate', ['ng'])
|
||||||
parent = after ? after.parent() : element.parent();
|
parent = after ? after.parent() : element.parent();
|
||||||
}
|
}
|
||||||
|
|
||||||
var disabledAnimation = { running : true };
|
|
||||||
var matches = lookup(animationLookup);
|
var matches = lookup(animationLookup);
|
||||||
var isClassBased = event == 'addClass' || event == 'removeClass';
|
var isClassBased = event == 'addClass' || event == 'removeClass';
|
||||||
var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
|
var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
|
||||||
|
|
@ -507,7 +505,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
//the element is not currently attached to the document body or then completely close
|
//the element is not currently attached to the document body or then completely close
|
||||||
//the animation if any matching animations are not found at all.
|
//the animation if any matching animations are not found at all.
|
||||||
//NOTE: IE8 + IE9 should close properly (run done()) in case a NO animation is not found.
|
//NOTE: IE8 + IE9 should close properly (run done()) in case a NO animation is not found.
|
||||||
if ((parent.inheritedData(NG_ANIMATE_STATE) || disabledAnimation).running || matches.length == 0) {
|
if (animationsDisabled(element, parent) || matches.length === 0) {
|
||||||
done();
|
done();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -528,7 +526,7 @@ angular.module('ngAnimate', ['ng'])
|
||||||
|
|
||||||
//this would mean that an animation was not allowed so let the existing
|
//this would mean that an animation was not allowed so let the existing
|
||||||
//animation do it's thing and close this one early
|
//animation do it's thing and close this one early
|
||||||
if(animations.length == 0) {
|
if(animations.length === 0) {
|
||||||
onComplete && onComplete();
|
onComplete && onComplete();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -622,8 +620,39 @@ angular.module('ngAnimate', ['ng'])
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanup(element) {
|
function cleanup(element) {
|
||||||
element.removeClass(NG_ANIMATE_CLASS_NAME);
|
if(element[0] == $rootElement[0]) {
|
||||||
element.removeData(NG_ANIMATE_STATE);
|
if(!rootAnimateState.disabled) {
|
||||||
|
rootAnimateState.running = false;
|
||||||
|
rootAnimateState.structural = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
element.removeClass(NG_ANIMATE_CLASS_NAME);
|
||||||
|
element.removeData(NG_ANIMATE_STATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function animationsDisabled(element, parent) {
|
||||||
|
if(element == $rootElement) {
|
||||||
|
return rootAnimateState.disabled || rootAnimateState.running;
|
||||||
|
}
|
||||||
|
|
||||||
|
var validState;
|
||||||
|
do {
|
||||||
|
//the element did not reach the root element which means that it
|
||||||
|
//is not apart of the DOM. Therefore there is no reason to do
|
||||||
|
//any animations on it
|
||||||
|
if(parent.length === 0 || parent[0] == $document[0]) return true;
|
||||||
|
|
||||||
|
var state = parent.data(NG_ANIMATE_STATE);
|
||||||
|
if(state && (state.disabled != null || state.running != null)) {
|
||||||
|
validState = state;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while(parent = parent.parent());
|
||||||
|
|
||||||
|
return validState ? (validState.disabled || validState.running) : true;
|
||||||
}
|
}
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,70 @@ describe("ngAnimate", function() {
|
||||||
expect($animate.enabled(1)).toBe(true);
|
expect($animate.enabled(1)).toBe(true);
|
||||||
expect($animate.enabled()).toBe(true);
|
expect($animate.enabled()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should place a hard disable on all child animations', function() {
|
||||||
|
var count = 0;
|
||||||
|
module(function($animateProvider) {
|
||||||
|
$animateProvider.register('.animated', function() {
|
||||||
|
return {
|
||||||
|
addClass : function(element, className, done) {
|
||||||
|
count++;
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
inject(function($compile, $rootScope, $animate, $sniffer, $rootElement, $timeout) {
|
||||||
|
$animate.enabled(true);
|
||||||
|
|
||||||
|
var elm1 = $compile('<div class="animated"></div>')($rootScope);
|
||||||
|
var elm2 = $compile('<div class="animated"></div>')($rootScope);
|
||||||
|
$rootElement.append(elm1);
|
||||||
|
angular.element(document.body).append($rootElement);
|
||||||
|
|
||||||
|
$animate.addClass(elm1, 'klass');
|
||||||
|
expect(count).toBe(1);
|
||||||
|
|
||||||
|
$animate.enabled(false);
|
||||||
|
|
||||||
|
$animate.addClass(elm1, 'klass2');
|
||||||
|
expect(count).toBe(1);
|
||||||
|
|
||||||
|
$animate.enabled(true);
|
||||||
|
|
||||||
|
elm1.append(elm2);
|
||||||
|
|
||||||
|
$animate.addClass(elm2, 'klass');
|
||||||
|
expect(count).toBe(2);
|
||||||
|
|
||||||
|
$animate.enabled(false, elm1);
|
||||||
|
|
||||||
|
$animate.addClass(elm2, 'klass2');
|
||||||
|
expect(count).toBe(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should skip animations if the element is attached to the $rootElement', function() {
|
||||||
|
var count = 0;
|
||||||
|
module(function($animateProvider) {
|
||||||
|
$animateProvider.register('.animated', function() {
|
||||||
|
return {
|
||||||
|
addClass : function(element, className, done) {
|
||||||
|
count++;
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
inject(function($compile, $rootScope, $animate, $sniffer, $rootElement, $timeout) {
|
||||||
|
$animate.enabled(true);
|
||||||
|
|
||||||
|
var elm1 = $compile('<div class="animated"></div>')($rootScope);
|
||||||
|
|
||||||
|
$animate.addClass(elm1, 'klass2');
|
||||||
|
expect(count).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("with polyfill", function() {
|
describe("with polyfill", function() {
|
||||||
|
|
@ -1956,4 +2020,28 @@ describe("ngAnimate", function() {
|
||||||
expect(element.hasClass('red-add')).toBe(false);
|
expect(element.hasClass('red-add')).toBe(false);
|
||||||
expect(element.hasClass('yellow-add')).toBe(true);
|
expect(element.hasClass('yellow-add')).toBe(true);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should enable and disable animations properly on the root element', function() {
|
||||||
|
var count = 0;
|
||||||
|
module(function($animateProvider) {
|
||||||
|
$animateProvider.register('.animated', function() {
|
||||||
|
return {
|
||||||
|
addClass : function(element, className, done) {
|
||||||
|
count++;
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
inject(function($compile, $rootScope, $animate, $sniffer, $rootElement, $timeout) {
|
||||||
|
|
||||||
|
$rootElement.addClass('animated');
|
||||||
|
$animate.addClass($rootElement, 'green');
|
||||||
|
expect(count).toBe(1);
|
||||||
|
|
||||||
|
$animate.addClass($rootElement, 'red');
|
||||||
|
expect(count).toBe(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue