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

fix(ngAnimate): only copy over the animation options once #13578

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/ng/animateCss.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@ var $CoreAnimateCssProvider = function() {
this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {

return function(element, initialOptions) {
// we always make a copy of the options since
// there should never be any side effects on
// the input data when running `$animateCss`.
var options = copy(initialOptions);
// all of the animation functions should create
// a copy of the options data, however, if a
// parent service has already created a copy then
// we should stick to using that
var options = initialOptions || {};
if (!options.$$prepared) {
options = copy(options);
}

// there is no point in applying the styles since
// there is no animation that goes on at all in
Expand Down
14 changes: 8 additions & 6 deletions src/ngAnimate/animateCss.js
Original file line number Diff line number Diff line change
Expand Up @@ -447,10 +447,14 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
}

return function init(element, initialOptions) {
// we always make a copy of the options since
// there should never be any side effects on
// the input data when running `$animateCss`.
var options = copy(initialOptions);
// all of the animation functions should create
// a copy of the options data, however, if a
// parent service has already created a copy then
// we should stick to using that
var options = initialOptions || {};
if (!options.$$prepared) {
options = prepareAnimationOptions(copy(options));
}

var restoreStyles = {};
var node = getDomNode(element);
Expand All @@ -460,8 +464,6 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
return closeAndReturnNoopAnimator();
}

options = prepareAnimationOptions(options);

var temporaryStyles = [];
var classes = element.attr('class');
var styles = packageStyles(options);
Expand Down
22 changes: 22 additions & 0 deletions test/ng/animateCssSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,28 @@ describe("$animateCss", function() {
expect(copiedOptions).toEqual(initialOptions);
}));

it("should not create a copy of the provided options if they have already been prepared earlier",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to provoke the unwanted render behavior instead? That the options are copied seems like an implementation detail to me.

inject(function($animateCss, $$rAF) {

var options = {
from: { height: '50px' },
to: { width: '50px' },
addClass: 'one',
removeClass: 'two'
};

options.$$prepared = true;
var runner = $animateCss(element, options).start();
runner.end();

$$rAF.flush();

expect(options.addClass).toBeFalsy();
expect(options.removeClass).toBeFalsy();
expect(options.to).toBeFalsy();
expect(options.from).toBeFalsy();
}));

it("should apply the provided [from] CSS to the element", inject(function($animateCss) {
$animateCss(element, { from: { height: '50px' }}).start();
expect(element.css('height')).toBe('50px');
Expand Down
22 changes: 22 additions & 0 deletions test/ngAnimate/animateCssSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1995,6 +1995,28 @@ describe("ngAnimate $animateCss", function() {
expect(copiedOptions).toEqual(initialOptions);
}));

it("should not create a copy of the provided options if they have already been prepared earlier",
inject(function($animate, $animateCss) {

var options = {
from: { height: '50px' },
to: { width: '50px' },
addClass: 'one',
removeClass: 'two'
};

options.$$prepared = true;
var runner = $animateCss(element, options).start();
runner.end();

$animate.flush();

expect(options.addClass).toBeFalsy();
expect(options.removeClass).toBeFalsy();
expect(options.to).toBeFalsy();
expect(options.from).toBeFalsy();
}));

describe("[$$skipPreparationClasses]", function() {
it('should not apply and remove the preparation classes to the element when true',
inject(function($animateCss) {
Expand Down
21 changes: 21 additions & 0 deletions test/ngAnimate/integrationSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,27 @@ describe('ngAnimate integration tests', function() {
describe('CSS animations', function() {
if (!browserSupportsCssAnimations()) return;

it("should only create a single copy of the provided animation options",
inject(function($rootScope, $rootElement, $animate) {

ss.addRule('.animate-me', 'transition:2s linear all;');

var element = jqLite('<div class="animate-me"></div>');
html(element);

var myOptions = {to: { 'color': 'red' }};

var spy = spyOn(window, 'copy');
expect(spy).not.toHaveBeenCalled();

var animation = $animate.leave(element, myOptions);
$rootScope.$digest();
$animate.flush();

expect(spy).toHaveBeenCalledOnce();
dealoc(element);
}));

they('should render an $prop animation',
['enter', 'leave', 'move', 'addClass', 'removeClass', 'setClass'], function(event) {

Expand Down