SuperVeetz
SuperVeetz

Reputation: 2176

Animating a button with AngularJS inside ng-repeat

I have this online shop here that is displaying items, sizes, prices, etc. and I want to add an animation to the .add-to-cart-btn <button> so that each time it is clicked, the button will perform some type of animation. I could do this with jQuery something like:

$(document).ready(function() {
  $('.add-to-cart-btn').on('click', function() {
    $(this).addClass('bounce').delay(1000).removeClass('bounce');
  });
});

but i'm trying to do things the "Angular Way" and I think i'm close. I have a directive here: thanks to This Post

angular.module('ForeverLeather').directive('animateTrigger', ['$animate', function ($animate) {

  return function (scope, elem, attrs) {
    elem.on('click', function (elem) {
      scope.$apply(function() {
        var el = angular.element(document.getElementsByClassName("add-to-cart-btn"));
        var promise = $animate.addClass(el, "bounce");
        promise.then(function () {
          scope.$apply(function() {
            $animate.removeClass(el, "bounce");
          });
        });
      });
    });
  }
}]);

HTML

  <div class="col-md-4 col-sm-6" ng-repeat="item in templateItems">

      <div class="product-img-wrapper">
        <img ng-src="{{item.src}}">
      </div>

      <h3 class="product-header">{{item.name}}</h3>
      <p class="lead product-text">
        {{item.description}}
      </p>

      <div class="product-btn-wrapper">
        <select ng-options="size.size as size.text for size in item.sizes" ng-model="item.selectedItem.size"
                class="form-control" ng-change="getPrice(item, item.selectedItem.size)">
          <option value="" disabled>-- Select to view prices --</option>
        </select>
        <span class="text-left product-price">{{item.selectedItem.price | currency}}</span>
        <button ng-click="addToCart(item.type, item.name, item.selectedItem.size);" 
                class="btn btn-lg btn-success add-to-cart-btn animated" animate-trigger>
          <i class="fa fa-cart-plus"></i>&nbsp; Add To Cart
        </button>
      </div>

    </div>

This works.. well kinda, my problem is that var el = angular.element(document.getElementsByClassName("add-to-cart-btn")); always find the first button on the page with this class name, I need to change this to something like:

var el = angular.element(elem); // invalid
var el = angular.element(this); // invalid

so that I am performing the animation on the currently clicked element instead of the first of the page.

Upvotes: 3

Views: 1122

Answers (2)

Kevin Beal
Kevin Beal

Reputation: 10849

Animations in AngularJS are meant to be used another way.

There are 4 types of animation events

  • enter
  • leave
  • move
  • add/remove class

If you want to do it the 'Angular way' with javascript, then what you want to do is hook into these events.

What you want to target in this case is the button like this:

(From the docs: https://docs.angularjs.org/api/ngAnimate)

myModule.animation('.slide', [function() {
  return {
    // make note that other events (like addClass/removeClass)
    // have different function input parameters
    enter: function(element, doneFn) {
      jQuery(element).fadeIn(1000, doneFn);

      // remember to call doneFn so that angular
      // knows that the animation has concluded
    },

    move: function(element, doneFn) {
      jQuery(element).fadeIn(1000, doneFn);
    },

    leave: function(element, doneFn) {
      jQuery(element).fadeOut(1000, doneFn);
    }
  }
}]

In order to hook into an event on-click, then enter, leave and move won't do you much good; what you want is to add or remove a class name on the click event. You would could add the class in a number of ways, but here's one example:

<button ng-class="{'my-animation':model.animationOn}" ng-click="model.animationOn=true">My Button Text</button>

You'd have to remove the class at some point, though. The most appropriate time would probably be in the callback for that event using Angular's animation API. Something like this:

myModule.animation('.my-btn', [function() {
  return {
    addClass: function(element, className, doneFn) {
      if(className == 'my-animation') {
        jQuery(element).fadeIn(1000, function() {
          doneFn(); // always call this when you're done
          element.removeClass('my-btn');
        });
      }
    }
  }
}]

Also, in order for Angular to know about you adding and removing classes in javascript, you have to use one of the directives that ship with Angular, or use the $animate service in angular. In your first code example, you are adding a class using jQuery, but Angular will not know about it.

Upvotes: 2

Jan
Jan

Reputation: 5815

But you already have the element, all you need is

return function (scope, elem, attrs) {
    elem.on('click', function () {
      scope.$apply(function() {
        var promise = $animate.addClass(elem, "bounce");
        promise.then(function () {
          scope.$apply(function() {
            $animate.removeClass(elem, "bounce");
          });
        });
      });
    });
  }

Upvotes: 4

Related Questions