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

Commit d5683d2

Browse files
committed
fix($animateCss): ensure that an object is always returned even when no animation is set to run
Before in RC0 and RC1 $animateCss would not return anything if a CSS-based animation was not detected. This was a messy API decision which resulted in the user having to have an if statement to handle the failure case. This patch ensures that an animator object with the start() and end() functions is always returned. If an animation is not detected then the preperatory CSS styles and classes are removed immediately and the element is cleaned up, however a "dump" animator object is still returned which allows for callbacks and promises to be applied. The returned object now also contains a `valid` property which can be examined to determine whether an animation is set to run on the element. BREAKING CHANGE: The $animateCss service will now always return an object even if the animation is not set to run. If your code is using $animateCss then please consider the following code change: ``` // before var animator = $animateCss(element, { ... }); if (!animator) { continueApp(); return; } var runner = animator.start(); runner.done(continueApp); runner.then(continueApp); // now var animator = $animateCss(element, { ... }); var runner = animator.start(); runner.done(continueApp); runner.then(continueApp); ```
1 parent df24410 commit d5683d2

File tree

5 files changed

+178
-72
lines changed

5 files changed

+178
-72
lines changed

src/ngAnimate/animateCss.js

+34-33
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,12 @@
3939
* return {
4040
* enter: function(element, doneFn) {
4141
* var height = element[0].offsetHeight;
42-
* var animation = $animateCss(element, {
42+
* var animator = $animateCss(element, {
4343
* from: { height:'0px' },
4444
* to: { height:height + 'px' },
4545
* duration: 1 // one second
4646
* });
47-
*
48-
* // if no possible animation can be triggered due
49-
* // to the combination of options then `animation`
50-
* // will be returned as undefined
51-
* animation.start().done(doneFn);
47+
* animator.start().done(doneFn);
5248
* }
5349
* }
5450
* }]);
@@ -71,18 +67,14 @@
7167
* return {
7268
* enter: function(element, doneFn) {
7369
* var height = element[0].offsetHeight;
74-
* var animation = $animateCss(element, {
70+
* var animator = $animateCss(element, {
7571
* addClass: 'red large-text pulse-twice',
7672
* easing: 'ease-out',
7773
* from: { height:'0px' },
7874
* to: { height:height + 'px' },
7975
* duration: 1 // one second
8076
* });
81-
*
82-
* // if no possible animation can be triggered due
83-
* // to the combination of options then `animation`
84-
* // will be returned as undefined
85-
* animation.start().done(doneFn);
77+
* animator.start().done(doneFn);
8678
* }
8779
* }
8880
* }]);
@@ -122,10 +114,11 @@
122114
* styles using the `from` and `to` properties.
123115
*
124116
* ```js
125-
* var animation = $animateCss(element, {
117+
* var animator = $animateCss(element, {
126118
* from: { background:'red' },
127119
* to: { background:'blue' }
128120
* });
121+
* animator.start();
129122
* ```
130123
*
131124
* ```css
@@ -158,10 +151,10 @@
158151
* added and removed on the element). Once `$animateCss` is called it will return an object with the following properties:
159152
*
160153
* ```js
161-
* var animation = $animateCss(element, { ... });
154+
* var animator = $animateCss(element, { ... });
162155
* ```
163156
*
164-
* Now what do the contents of our `animation` variable look like:
157+
* Now what do the contents of our `animator` variable look like:
165158
*
166159
* ```js
167160
* {
@@ -185,18 +178,11 @@
185178
* The example below should put this into perspective:
186179
*
187180
* ```js
188-
* var animation = $animateCss(element, { ... });
189-
*
190-
* // remember that if there is no CSS animation detected on the element
191-
* // then the value returned from $animateCss will be null
192-
* if (animation) {
193-
* animation.start().done(function() {
194-
* // yaay the animation is over
195-
* doneCallback();
196-
* });
197-
* } else {
181+
* var animator = $animateCss(element, { ... });
182+
* animator.start().done(function() {
183+
* // yaay the animation is over
198184
* doneCallback();
199-
* }
185+
* });
200186
* ```
201187
*
202188
* @param {DOMElement} element the element that will be animated
@@ -223,7 +209,7 @@
223209
* `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`)
224210
* `applyClassesEarly` - Whether or not the classes being added or removed will be used when detecting the animation. This is set by `$animate` when enter/leave/move animations are fired to ensure that the CSS classes are resolved in time. (Note that this will prevent any transitions from occuring on the classes being added and removed.)
225211
*
226-
* @return {null|object} an object with a start method and details about the animation. If no animation is detected then a value of `null` will be returned.
212+
* @return {object} an object with start and end methods and details about the animation.
227213
*
228214
* * `start` - The method to start the animation. This will return a `Promise` when called.
229215
* * `end` - This method will cancel the animation and remove all applied CSS classes and styles.
@@ -538,8 +524,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
538524
var maxDurationTime;
539525

540526
if (options.duration === 0 || (!$sniffer.animations && !$sniffer.transitions)) {
541-
close();
542-
return;
527+
return closeAndReturnNoopAnimator();
543528
}
544529

545530
var method = options.event && isArray(options.event)
@@ -586,8 +571,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
586571
// there is no way we can trigger an animation since no styles and
587572
// no classes are being applied which would then trigger a transition
588573
if (!hasToStyles && !setupClasses) {
589-
close();
590-
return false;
574+
return closeAndReturnNoopAnimator();
591575
}
592576

593577
var cacheKey, stagger;
@@ -682,8 +666,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
682666
}
683667

684668
if (maxDuration === 0 && !flags.recalculateTimingStyles) {
685-
close();
686-
return false;
669+
return closeAndReturnNoopAnimator();
687670
}
688671

689672
// we need to recalculate the delay value since we used a pre-emptive negative
@@ -711,6 +694,7 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
711694

712695
// TODO(matsko): for 1.5 change this code to have an animator object for better debugging
713696
return {
697+
$$willAnimate: true,
714698
end: endFn,
715699
start: function() {
716700
if (animationClosed) return;
@@ -790,6 +774,23 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
790774
}
791775
}
792776

777+
function closeAndReturnNoopAnimator() {
778+
runner = new $$AnimateRunner({
779+
end: endFn,
780+
cancel: cancelFn
781+
});
782+
783+
close();
784+
785+
return {
786+
$$willAnimate: false,
787+
start: function() {
788+
return runner;
789+
},
790+
end: endFn
791+
};
792+
}
793+
793794
function start() {
794795
if (animationClosed) return;
795796

src/ngAnimate/animateCssDriver.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,15 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
130130
}
131131

132132
function prepareOutAnimation() {
133-
return $animateCss(clone, {
133+
var animator = $animateCss(clone, {
134134
addClass: NG_OUT_ANCHOR_CLASS_NAME,
135135
delay: true,
136136
from: calculateAnchorStyles(outAnchor)
137137
});
138+
139+
// read the comment within `prepareRegularAnimation` to understand
140+
// why this check is necessary
141+
return animator.$$willAnimate ? animator : null;
138142
}
139143

140144
function getClassVal(element) {
@@ -146,12 +150,16 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
146150
var toAdd = getUniqueValues(endingClasses, startingClasses);
147151
var toRemove = getUniqueValues(startingClasses, endingClasses);
148152

149-
return $animateCss(clone, {
153+
var animator = $animateCss(clone, {
150154
to: calculateAnchorStyles(inAnchor),
151155
addClass: NG_IN_ANCHOR_CLASS_NAME + ' ' + toAdd,
152156
removeClass: NG_OUT_ANCHOR_CLASS_NAME + ' ' + toRemove,
153157
delay: true
154158
});
159+
160+
// read the comment within `prepareRegularAnimation` to understand
161+
// why this check is necessary
162+
return animator.$$willAnimate ? animator : null;
155163
}
156164

157165
function end() {
@@ -232,7 +240,13 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
232240
options.onDone = animationDetails.domOperation;
233241
}
234242

235-
return $animateCss(element, options);
243+
var animator = $animateCss(element, options);
244+
245+
// the driver lookup code inside of $$animation attempts to spawn a
246+
// driver one by one until a driver returns a.$$willAnimate animator object.
247+
// $animateCss will always return an object, however, it will pass in
248+
// a flag as a hint as to whether an animation was detected or not
249+
return animator.$$willAnimate ? animator : null;
236250
}
237251
}];
238252
}];

src/ngAnimate/module.js

+9-18
Original file line numberDiff line numberDiff line change
@@ -363,17 +363,12 @@
363363
* myModule.animation('.slide', ['$animateCss', function($animateCss) {
364364
* return {
365365
* enter: function(element, doneFn) {
366-
* var animation = $animateCss(element, {
367-
* event: 'enter'
368-
* });
369-
*
370-
* if (animation) {
371-
* // this will trigger `.slide.ng-enter` and `.slide.ng-enter-active`.
372-
* var runner = animation.start();
373-
* runner.done(doneFn);
374-
* } else { //no CSS animation was detected
375-
* doneFn();
376-
* }
366+
* // this will trigger `.slide.ng-enter` and `.slide.ng-enter-active`.
367+
* var runner = $animateCss(element, {
368+
* event: 'enter',
369+
* structural: true
370+
* }).start();
371+
* runner.done(doneFn);
377372
* }
378373
* }
379374
* }]
@@ -389,18 +384,14 @@
389384
* myModule.animation('.slide', ['$animateCss', function($animateCss) {
390385
* return {
391386
* enter: function(element, doneFn) {
392-
* var animation = $animateCss(element, {
387+
* var runner = $animateCss(element, {
393388
* event: 'enter',
394389
* addClass: 'maroon-setting',
395390
* from: { height:0 },
396391
* to: { height: 200 }
397-
* });
392+
* }).start();
398393
*
399-
* if (animation) {
400-
* animation.start().done(doneFn);
401-
* } else {
402-
* doneFn();
403-
* }
394+
* runner.done(doneFn);
404395
* }
405396
* }
406397
* }]

test/ngAnimate/animateCssDriverSpec.js

+14-9
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ describe("ngAnimate $$animateCssDriver", function() {
5858
});
5959

6060
return {
61+
$$willAnimate: true,
6162
start: function() {
6263
return runner;
6364
}
@@ -124,7 +125,9 @@ describe("ngAnimate $$animateCssDriver", function() {
124125

125126
it("should not return anything if no animation is detected", function() {
126127
module(function($provide) {
127-
$provide.value('$animateCss', noop);
128+
$provide.value('$animateCss', function() {
129+
return { $$willAnimate: false };
130+
});
128131
});
129132
inject(function() {
130133
var runner = driver({
@@ -151,6 +154,7 @@ describe("ngAnimate $$animateCssDriver", function() {
151154
$provide.factory('$animateCss', function($q, $$AnimateRunner) {
152155
return function() {
153156
return {
157+
$$willAnimate: true,
154158
start: function() {
155159
return new $$AnimateRunner({
156160
end: function() {
@@ -190,6 +194,7 @@ describe("ngAnimate $$animateCssDriver", function() {
190194
var type = options.event || 'anchor';
191195
closeLog[type] = closeLog[type] || [];
192196
return {
197+
$$willAnimate: true,
193198
start: function() {
194199
return new $$AnimateRunner({
195200
end: function() {
@@ -252,6 +257,7 @@ describe("ngAnimate $$animateCssDriver", function() {
252257
$provide.factory('$animateCss', function($$AnimateRunner) {
253258
return function(element, details) {
254259
return {
260+
$$willAnimate: true,
255261
start: function() {
256262
animationLog.push([element, details.event]);
257263
return new $$AnimateRunner();
@@ -429,14 +435,13 @@ describe("ngAnimate $$animateCssDriver", function() {
429435
$provide.factory('$animateCss', function($$AnimateRunner) {
430436
return function(element, options) {
431437
var addClass = (options.addClass || '').trim();
432-
if (addClass === expectedClass) {
433-
return {
434-
start: function() {
435-
animationStarted = addClass;
436-
return runner = new $$AnimateRunner();
437-
}
438-
};
439-
}
438+
return {
439+
$$willAnimate: addClass === expectedClass,
440+
start: function() {
441+
animationStarted = addClass;
442+
return runner = new $$AnimateRunner();
443+
}
444+
};
440445
};
441446
});
442447
});

0 commit comments

Comments
 (0)