Stop Angular Animation from happening on ng-show / ng-hide
Solution 1
I think the best way to do this is to use the $animateProvider.classNameFilter
which will allow you to filter items to animate or in this case not to animate. We'll do something like:
$animateProvider.classNameFilter(/^((?!(fa-spinner)).)*$/);
//$animateProvider.classNameFilter(/^((?!(fa-spinner|class2|class3)).)*$/);
Demo:
http://plnkr.co/edit/lbqviQ87MQoZWeXplax1?p=preview
Angular docs state:
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.
As another answer per the comment with the no-animate
directive, you could write an ng-show
directive that will run at a higher priority and disable the animation. We will only do this if the element has the fa-spinner
class.
problemApp.directive('ngShow', function($compile, $animate) {
return {
priority: 1000,
link: function(scope, element, attrs) {
if (element.hasClass('fa-spinner')) {
// we could add no-animate and $compile per
// http://stackoverflow.com/questions/23879654/angularjs-exclude-certain-elements-from-animations?rq=1
// or we can just include that no-animate directive's code here
$animate.enabled(false, element)
scope.$watch(function() {
$animate.enabled(false, element)
})
}
}
}
});
Demo: http://plnkr.co/edit/BYrhEompZAF5RKxU7ifJ?p=preview
Lastly, and similar to the above, we can use the no-animate
directive if we want to make it a little more modular. In this case I'm naming the directive faSpin
which you could do in the answer above. This is essentially the same only we are preserving the directive from the SO answer mentioned in the comment of the above codeset. If you only care about the fa-spin
animation issues naming it this way works well, but if you have other ng-show
animation problems you'd want to name it ngShow
and add to the if clause.
problemApp.directive('noAnimate', ['$animate',
function($animate) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
$animate.enabled(false, element)
scope.$watch(function() {
$animate.enabled(false, element)
})
}
};
}
])
problemApp.directive('faSpin', function($compile, $animate) {
return {
priority: 1000,
link: function(scope, element, attrs) {
if (element.hasClass('fa-spinner')) {
element.attr('no-animate', true);
$compile(element)(scope);
}
}
}
});
Demo: http://plnkr.co/edit/P3R74mUR27QUyxMFcyf4?p=preview
Solution 2
I had a similar problem where my icon would keep spinning until the animation finished, even after the $scope variable turned off. What worked for me was to wrap the <i>
fa-icon element in a span.
<span ng-if="loading"><i class="fa fa-refresh fa-spin"></i></span>
Try it!
Solution 3
I've found an easier way.
<i class="fa fa-spinner" ng-show="loading" ng-class="{'fa-spin' : loading}"></i>
Forked plunker: http://plnkr.co/edit/mCsw5wBL1bsLYB7dCtQF
I did run into another small issue as a result of doing this where the icon would appear out of position if it spun for more than 2 seconds, but that was caused by the 'ng-hide-add-active' class, so I just added in my css:
.fa-spinner.ng-hide-add-active {
display: none !important;
}
and that took care of it.
EDIT: Nico's solution is a slightly cleaner version of this, so I'd consider using his.
Solution 4
angular.module('myCoolAppThatIsAwesomeAndGreat')
.config(function($animateProvider) {
// ignore animations for any element with class `ng-animate-disabled`
$animateProvider.classNameFilter(/^((?!(ng-animate-disabled)).)*$/);
});
Then you can just add the class ng-animate-disabled
to any element you want.
<button><i class="fa fa-spinner ng-animate-disabled" ng-show="somethingTrue"></i></button>
Related videos on Youtube
Comments
-
Chris Lees about 4 years
In my AngularJS application I'm using fontawesome for my loading spinners:
<i class="fa fa-spin fa-spinner" ng-show="loading"></i>
I'm also using AngularToaster for notification messages which has a dependency on ngAnimate. When I include ngAnimate in my AngularJS application, it messes up my loading spinners by animating them in a weird way. I want to stop this from happening but cant find a way to disable the animation on just these loaders (it would also stink to have to update every loader I have in my app).
Heres a plunkr showing my exact problem.
-
Chris Lees almost 10 yearsI came across this answer, but I'd rather not decorate all of my loaders with a "no-animate" directive stackoverflow.com/questions/23879654/…
-
lucuma almost 10 yearsWhat about creating your own show directive?
-
Chris Lees almost 10 years@lucuma I'd rather not reinvent the wheel if I could avoid it. Plus that also involves me updating all my loaders with this new ng-show directive. My ideal solution would be to configure it in one place and forget about it.
-
Jason Goemaat almost 10 yearsThis exposes one of the issues with automatic animation in 1.2. I keep hoping they'll go back to the declarative way they had in 1.1. I don't think angular should animate elements unless I specifically tell them what to animate.
-
lucuma almost 10 years@JasonGoemaat, because Angular is so modular it isn't so much an issue but just keeping up with the changes. I didn't know until I just researched it about the classNameFilter which gives you a lot of control over what is animated.
-
-
lucuma almost 10 yearsNo problem. Out of curiosity which solution are you going to use?
-
Chris Lees almost 10 yearsDefinitely the $animateProvider solution. I can do it in one place and have it effect my whole application. Thanks so much for providing multiple solutions though. It helps me understand a couple different places I can hook into in AngularJS that I never thought of.
-
qmo over 9 yearsThanks for the solution. I used $animateProvider with fa-spin instad of fa-spinner
-
parliament about 9 yearsthat css worked for me like a charm. this is truly the best fix for the problem.
-
westor over 8 yearsI do not understand your no-animate directive: what are you watching for? The function in first parameter of scope.watch should return the watch value, yes?
-
lucuma over 8 yearsThe second directive which by the way was a copy/paste from Angular's no-animate directive will fire anytime there is a DOM change I believe.
-
stevo over 8 yearsThanks @lucuma!! I used $animateProvider with fa-spin instad of fa-spinner solution too. Many Thanks!
-
Ursin Brunner over 8 yearsSimple and easy... just place a <span> around the element you animate and apply the ng-if there.
-
Chris Lees about 8 yearsThis is great, but the problem here is, you'll need to add the ng-class directive to every one of your loader elements. It fixes the problem, but if you have a lot of spinners throughout your application, it could get messy updating them all.
-
James Fiala about 8 years@ChrisLees true- I didn't even think about that, but that's because we created a spinner directive that encapsulated this and then just used that one spinner everywhere. Honestly, I like nicotr014's answer better now anyways- it's really just a spin-off of this answer.
-
Chris Lees about 8 years@JamesFiala I do like the idea of creating a spinner directive to use everywhere. Staying consistent with variable names this could be pretty handy to have.
-
RobertoNovelo about 8 yearsWhy bother adding JS when you can place a simple span around the element you want to hide/show. I think this is definitely what angular tries to achieve.
-
Fizer Khan about 7 years@ChrisLees You dont need to update ng-class. Instead, you can have
fa-spin
inside the class instead of ng-class.<i class="fa fa-spinner fa-spin" ng-show="loading">
-
kotpal over 6 yearsThis should be the accepted answer - no need to use a directive or some other JavaScript code. Simple and effective
-
ammills01 about 6 yearsThis worked for me. In my case the <i> is already wrapped in a <div> that is hidden/shown based on a boolean. The issue I was seeing is when someone would click the button it would show this icon and start to spin, but would stop spinning after a second or so. Wrapping the <i> in a <span> solved it. Thanks!
-
Riples over 3 yearsWorked perfectly! Thanks