From d40909b8d660c3b1389c0163fc79c9c913b4535f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Mon, 4 May 2015 21:20:10 -0700 Subject: [PATCH 1/2] docs(ngAnimate): add docs for the usage of the `ng-animate` CSS class --- src/ngAnimate/module.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/ngAnimate/module.js b/src/ngAnimate/module.js index 9d54b4faad5a..cdc5a8c84677 100644 --- a/src/ngAnimate/module.js +++ b/src/ngAnimate/module.js @@ -224,6 +224,35 @@ * * Stagger animations are currently only supported within CSS-defined animations. * + * ### The `ng-animate` CSS class + * + * When ngAnimate is animating an element it will apply the `ng-animate` CSS class to the element for the duration of the animation. + * This is a temporary CSS class and it will be removed once the animation is over (for both JavaScript and CSS-based animations). + * + * Therefore, animations can be applied to an element using this temporary class directly via CSS. + * + * ```css + * .zipper.ng-animate { + * transition:0.5s linear all; + * } + * .zipper.ng-enter { + * opacity:0; + * } + * .zipper.ng-enter.ng-enter-active { + * opacity:1; + * } + * .zipper.ng-leave { + * opacity:1; + * } + * .zipper.ng-leave.ng-leave-active { + * opacity:0; + * } + * ``` + * + * (Note that the `ng-animate` CSS class is reserved and it cannot be applied on an element directly since ngAnimate will always remove + * the CSS class once an animation has completed.) + * + * * ## JavaScript-based Animations * * ngAnimate also allows for animations to be consumed by JavaScript code. The approach is similar to CSS-based animations (where there is a shared From 1e1f49cca4648c871004a2596d92474b7dc0974a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Mon, 4 May 2015 21:41:34 -0700 Subject: [PATCH 2/2] fix(ngAnimate): prohibit usage of the `ng-animate` class with classNameFilter Since ngAnimate uses the `ng-animate` CSS class internally to track state it is better to keep this as a reserved CSS class to avoid accidentally adding / removing the CSS class when an animation is started and closed. BREAKING CHANGE: partially or fully using a regex value containing `ng-animate` as a token is not allowed anymore. Doing so will trigger a minErr exception to be thrown. So don't do this: ```js // only animate elements that contain the `ng-animate` CSS class $animateProvider.classNameFilter(/ng-animate/); // or partially contain it $animateProvider.classNameFilter(/some-class ng-animate another-class/); // but this is OK $animateProvider.classNameFilter(/ng-animate-special/); ``` Closes #11431 --- src/ng/animate.js | 8 ++++++++ src/ngAnimate/.jshintrc | 1 + src/ngAnimate/animation.js | 1 - src/ngAnimate/shared.js | 1 + test/ngAnimate/animateSpec.js | 21 +++++++++++++++++++++ 5 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/ng/animate.js b/src/ng/animate.js index e105de1ab28f..edb03e83c0a8 100644 --- a/src/ng/animate.js +++ b/src/ng/animate.js @@ -2,6 +2,7 @@ var $animateMinErr = minErr('$animate'); var ELEMENT_NODE = 1; +var NG_ANIMATE_CLASSNAME = 'ng-animate'; function mergeClasses(a,b) { if (!a && !b) return ''; @@ -231,6 +232,13 @@ var $AnimateProvider = ['$provide', function($provide) { this.classNameFilter = function(expression) { if (arguments.length === 1) { this.$$classNameFilter = (expression instanceof RegExp) ? expression : null; + if (this.$$classNameFilter) { + var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)"); + if (reservedRegex.test(this.$$classNameFilter.toString())) { + throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME); + + } + } } return this.$$classNameFilter; }; diff --git a/src/ngAnimate/.jshintrc b/src/ngAnimate/.jshintrc index ff0fac6abb0b..f3806a6bb976 100644 --- a/src/ngAnimate/.jshintrc +++ b/src/ngAnimate/.jshintrc @@ -20,6 +20,7 @@ "isElement": false, "ELEMENT_NODE": false, + "NG_ANIMATE_CLASSNAME": false, "NG_ANIMATE_CHILDREN_DATA": false, "assertArg": false, diff --git a/src/ngAnimate/animation.js b/src/ngAnimate/animation.js index 0c52760d56bd..4f746e14782a 100644 --- a/src/ngAnimate/animation.js +++ b/src/ngAnimate/animation.js @@ -1,7 +1,6 @@ 'use strict'; var $$AnimationProvider = ['$animateProvider', function($animateProvider) { - var NG_ANIMATE_CLASSNAME = 'ng-animate'; var NG_ANIMATE_REF_ATTR = 'ng-animate-ref'; var drivers = this.drivers = []; diff --git a/src/ngAnimate/shared.js b/src/ngAnimate/shared.js index bfa5a80c5916..1488dea1e409 100644 --- a/src/ngAnimate/shared.js +++ b/src/ngAnimate/shared.js @@ -16,6 +16,7 @@ var isElement = angular.isElement; var ELEMENT_NODE = 1; var COMMENT_NODE = 8; +var NG_ANIMATE_CLASSNAME = 'ng-animate'; var NG_ANIMATE_CHILDREN_DATA = '$$ngAnimateChildren'; var isPromiseLike = function(p) { diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js index cf4d4fefd45a..ff534759747d 100644 --- a/test/ngAnimate/animateSpec.js +++ b/test/ngAnimate/animateSpec.js @@ -148,6 +148,27 @@ describe("animations", function() { }); }); + it('should throw a minErr if a regex value is used which partially contains or fully matches the `ng-animate` CSS class', function() { + module(function($animateProvider) { + assertError(/ng-animate/, true); + assertError(/first ng-animate last/, true); + assertError(/ng-animate-special/, false); + assertError(/first ng-animate-special last/, false); + assertError(/first ng-animate ng-animate-special last/, true); + + function assertError(regex, bool) { + var expectation = expect(function() { + $animateProvider.classNameFilter(regex); + }); + + var message = '$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "ng-animate" CSS class.'; + + bool ? expectation.toThrowMinErr('$animate', 'nongcls', message) + : expectation.not.toThrowMinErr('$animate', 'nongcls', message); + } + }); + }); + it('should complete the leave DOM operation in case the classNameFilter fails', function() { module(function($animateProvider) { $animateProvider.classNameFilter(/memorable-animation/);