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

Commit c2b97c4

Browse files
committed
fix(ngAnimate): run structural animations with cancelled out class changes
When multiple animations on the same element are queued before a $digest passes, the animator tries to create as few actual animations as possible by joining / canceling redundant animations. Class-based animations for example are cancelled when the classes that are added and removed are the same, and the result is no class-change. This however must only happen if there's no structural animation currently queued. Fixes #14249
1 parent 82a4545 commit c2b97c4

File tree

3 files changed

+64
-2
lines changed

3 files changed

+64
-2
lines changed

src/ngAnimate/animateQueue.js

+5
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
8282
});
8383

8484
rules.cancel.push(function(element, newAnimation, currentAnimation) {
85+
// cancel the animation if classes added / removed in both animation cancel each other out,
86+
// but only if the current animation isn't structural
87+
88+
if (currentAnimation.structural) return false;
89+
8590
var nA = newAnimation.addClass;
8691
var nR = newAnimation.removeClass;
8792
var cA = currentAnimation.addClass;

test/ngAnimate/animateSpec.js

+26-2
Original file line numberDiff line numberDiff line change
@@ -1104,7 +1104,8 @@ describe("animations", function() {
11041104
$animate.removeClass(element, 'active-class');
11051105
$rootScope.$digest();
11061106

1107-
expect(doneHandler).toHaveBeenCalled();
1107+
// true = rejected
1108+
expect(doneHandler).toHaveBeenCalledWith(true);
11081109
}));
11091110

11101111
it('should cancel the previously running removeClass animation if a follow-up addClass animation is using the same class value',
@@ -1123,7 +1124,8 @@ describe("animations", function() {
11231124
$animate.addClass(element, 'active-class');
11241125
$rootScope.$digest();
11251126

1126-
expect(doneHandler).toHaveBeenCalled();
1127+
// true = rejected
1128+
expect(doneHandler).toHaveBeenCalledWith(true);
11271129
}));
11281130

11291131
it('should merge a follow-up animation that does not add classes into the previous animation (pre-digest)',
@@ -1198,6 +1200,28 @@ describe("animations", function() {
11981200

11991201
expect(capturedAnimation[2].addClass).toBe('blue');
12001202
}));
1203+
1204+
it('should NOT cancel a previously joined addClass+structural animation if a follow-up removeClass animation is using the same class value (pre-digest)',
1205+
inject(function($animate, $rootScope) {
1206+
1207+
var runner = $animate.enter(element, parent);
1208+
$animate.addClass(element, 'active-class');
1209+
1210+
var doneHandler = jasmine.createSpy('enter done');
1211+
runner.done(doneHandler);
1212+
1213+
expect(doneHandler).not.toHaveBeenCalled();
1214+
1215+
$animate.removeClass(element, 'active-class');
1216+
$rootScope.$digest();
1217+
1218+
expect(capturedAnimation[1]).toBe('enter');
1219+
expect(capturedAnimation[2].addClass).toBe(null);
1220+
expect(capturedAnimation[2].removeClass).toBe(null);
1221+
1222+
expect(doneHandler).not.toHaveBeenCalledWith(true);
1223+
}));
1224+
12011225
});
12021226

12031227
describe('should merge', function() {

test/ngAnimate/integrationSpec.js

+33
Original file line numberDiff line numberDiff line change
@@ -756,5 +756,38 @@ describe('ngAnimate integration tests', function() {
756756
expect(child.attr('style')).toContain('50px');
757757
});
758758
});
759+
760+
761+
it('should execute the enter animation on a <form> with ngIf that has a <input> of type="email" that is required', function() {
762+
763+
var animationSpy = jasmine.createSpy();
764+
765+
module(function($animateProvider) {
766+
$animateProvider.register('.animate-me', function() {
767+
return {
768+
enter: function(element, done) {
769+
animationSpy();
770+
done();
771+
}
772+
};
773+
});
774+
});
775+
776+
inject(function($animate, $rootScope, $compile) {
777+
var form;
778+
779+
element = jqLite('<div><form class="animate-me" ng-if="show"><input ng-model="myModel" type="email" required /></form></div>');
780+
html(element);
781+
782+
$compile(element)($rootScope);
783+
784+
$rootScope.show = true;
785+
$rootScope.$digest();
786+
form = element.find('form');
787+
788+
$animate.flush();
789+
expect(animationSpy).toHaveBeenCalled();
790+
});
791+
});
759792
});
760793
});

0 commit comments

Comments
 (0)