Stop Angular Animation from happening on ng-show / ng-hide

13,985

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>

Share:
13,985

Related videos on Youtube

Chris Lees
Author by

Chris Lees

Software Developer based in New York.

Updated on April 18, 2020

Comments

  • Chris Lees
    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.

    http://plnkr.co/edit/wVY5iSpUST52noIA2h5a

    • Chris Lees
      Chris Lees almost 10 years
      I came across this answer, but I'd rather not decorate all of my loaders with a "no-animate" directive stackoverflow.com/questions/23879654/…
    • lucuma
      lucuma almost 10 years
      What about creating your own show directive?
    • Chris Lees
      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
      Jason Goemaat almost 10 years
      This 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
      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
    lucuma almost 10 years
    No problem. Out of curiosity which solution are you going to use?
  • Chris Lees
    Chris Lees almost 10 years
    Definitely 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
    qmo over 9 years
    Thanks for the solution. I used $animateProvider with fa-spin instad of fa-spinner
  • parliament
    parliament about 9 years
    that css worked for me like a charm. this is truly the best fix for the problem.
  • westor
    westor over 8 years
    I 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
    lucuma over 8 years
    The 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
    stevo over 8 years
    Thanks @lucuma!! I used $animateProvider with fa-spin instad of fa-spinner solution too. Many Thanks!
  • Ursin Brunner
    Ursin Brunner over 8 years
    Simple and easy... just place a <span> around the element you animate and apply the ng-if there.
  • Chris Lees
    Chris Lees about 8 years
    This 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
    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
    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
    RobertoNovelo about 8 years
    Why 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
    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
    kotpal over 6 years
    This should be the accepted answer - no need to use a directive or some other JavaScript code. Simple and effective
  • ammills01
    ammills01 about 6 years
    This 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
    Riples over 3 years
    Worked perfectly! Thanks