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

feat(ngAnimate): provide configuration support to match specific className values to trigger animations #5448

Closed
wants to merge 2 commits 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
22 changes: 22 additions & 0 deletions src/ng/animate.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,28 @@ var $AnimateProvider = ['$provide', function($provide) {
$provide.factory(key, factory);
};

/**
* @ngdoc function
* @name ng.$animateProvider#classNameFilter
* @methodOf ng.$animateProvider
*
* @description
* Sets and/or returns the CSS class regular expression that is checked when performing
* an animation. Upon bootstrap the classNameFilter value is not set at all and will
* therefore enable $animate to attempt to perform an animation on any element.
* When setting the classNameFilter value, animations will only be performed on elements
* that successfully match the filter expression. This in turn can boost performance
* for low-powered devices as well as applications containing a lot of structural operations.
* @param {RegExp=} expression The className expression which will be checked against all animations
* @return {RegExp} The current CSS className expression value. If null then there is no expression value.
*/
this.classNameFilter = function(expression) {
if(arguments.length === 1) {
this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
}
return this.$$classNameFilter;
};

this.$get = ['$timeout', function($timeout) {

/**
Expand Down
19 changes: 15 additions & 4 deletions src/ngAnimate/animate.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,14 @@ angular.module('ngAnimate', ['ng'])
});
});

var isAnimatableClassName = function() { return true; };
var classNameFilter = $animateProvider.classNameFilter();
if(classNameFilter) {
isAnimatableClassName = function(className) {
return classNameFilter.test(className);
}
}

function lookup(name) {
if (name) {
var matches = [],
Expand Down Expand Up @@ -569,17 +577,20 @@ angular.module('ngAnimate', ['ng'])
and the onComplete callback will be fired once the animation is fully complete.
*/
function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {
var node = extractElementNode(element);
var currentClassName, classes, node = extractElementNode(element);
if(node) {
currentClassName = node.className;
classes = currentClassName + ' ' + className;
}

//transcluded directives may sometimes fire an animation using only comment nodes
//best to catch this early on to prevent any animation operations from occurring
if(!node) {
if(!node || !isAnimatableClassName(classes)) {
fireDOMOperation();
closeAnimation();
return;
}

var currentClassName = node.className;
var classes = currentClassName + ' ' + className;
var animationLookup = (' ' + classes).replace(/\s+/g,'.');
if (!parentElement) {
parentElement = afterElement ? afterElement.parent() : element.parent();
Expand Down
50 changes: 50 additions & 0 deletions test/ngAnimate/animateSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2947,5 +2947,55 @@ describe("ngAnimate", function() {
expect(capturedAnimation).toBe('leave');
});
});

it('should animate only the specified CSS className', function() {
var captures = {};
module(function($animateProvider) {
$animateProvider.classNameFilter(/prefixed-animation/);
$animateProvider.register('.capture', function() {
return {
enter : buildFn('enter'),
leave : buildFn('leave')
};

function buildFn(key) {
return function(element, className, done) {
captures[key] = true;
(done || className)();
}
}
});
});
inject(function($rootScope, $compile, $rootElement, $document, $timeout, $templateCache, $sniffer, $animate) {
if(!$sniffer.transitions) return;

var element = $compile('<div class="capture"></div>')($rootScope);
$rootElement.append(element);
jqLite($document[0].body).append($rootElement);

var enterDone = false;
$animate.enter(element, $rootElement, null, function() {
enterDone = true;
});

$rootScope.$digest();
$timeout.flush();

expect(captures['enter']).toBeUndefined();
expect(enterDone).toBe(true);

element.addClass('prefixed-animation');

var leaveDone = false;
$animate.leave(element, function() {
leaveDone = true;
});
$rootScope.$digest();
$timeout.flush();

expect(captures['leave']).toBe(true);
expect(leaveDone).toBe(true);
});
});
});
});