AngularJS provides hooks used to define a custom animation when a directive fires an addClass event. The following directives will generate addClass events:
f ngShow: This fires the addClass event after the ngShow expression evaluates to a truthy value, and just before the contents are set to visible
f ngHide: This fires the addClass event after the ngHide expression evaluates to a non-truthy value, and just before the contents are set to visible
f ngClass: This fires the addClass event just before the class is applied to the element f ngForm: This fires the addClass event to add validation classes
f ngModel: This fires the addClass event to add validation classes
f ngMessages: This is fired to add the ng-active class when one or more messages are visible, or to add the ng-inactive class when there are no messages
Getting ready
Suppose that you want to attach a fade-out animation to a piece of the DOM that has an ng-show directive. Remember that ng-show does not add or remove anything from the DOM; it merely toggles the CSS display property to set the visibility.
The initial setup, before animation is implemented, can be structured as follows:
(index.html) <div ng-app="myApp"> <div ng-controller="Ctrl"> <button ng-click="displayToggle=!displayToggle"> Toggle Visibility </button>
<div class="animate-container" ng-show="displayToggle"> Fade me out!
</div> </div>
</div> (app.js) angular.module('myApp', ['ngAnimate']) .controller('Ctrl',function($scope) { $scope.displayToggle = true; });
How to do it…
When the ng-show expression evaluates to false, the DOM element is immediately hidden. However, with the inclusion of the ngAnimate module, AngularJS will add in animation hooks, upon which you can define animations for how the element will be removed from the page. The animation can be defined by a CSS transition, CSS animation, or by JavaScript. The animation definition can be constructed in different ways. CSS transitions and CSS animations will use the addClass CSS class hooks to define the animation, whereas JavaScript animations will use the ngAnimate directive's addClass() method.
CSS transitions
Animating a fade-in effect with CSS transitions simply requires attaching opposite opacity values when the ng-hide class is added. Remember that ng-show and ng-hide are merely toggling the presence of this ng-hide class through the use of the addClass and removeClass animation events. This can be done as follows:
(style.css)
.animate-container.ng-hide-add { transition: all linear 1s; opacity: 1; } .animate-container.ng-hide-add.ng-hide-add-active { opacity: 0; } JSFiddle: http://jsfiddle.net/msfrisbie/bewso5sd/
CSS animation
Animating with a CSS animation is just as simple as CSS transitions, as follows:
(style.css) .animate-container.ng-hide-add { animation: 1s fade-out; } @keyframes fade-out { 0% { opacity: 1; } 100% { opacity: 0; } } JSFiddle: http://jsfiddle.net/msfrisbie/aez97r46/ JavaScript animation
Animating with JavaScript requires that you manually add and remove the relevant CSS classes, as well as explicitly call the animations. Since AngularJS and jqLite objects don't have an animation method, you will need to use the jQuery object's animate() method. This can be done as follows:
(app.js) angular.module('myApp', ['ngAnimate']) .controller('Ctrl', function($scope) { $scope.displayToggle = true; }) .animation('.animate-container', function() { return {
addClass: function(element, className, done) { if (className==='ng-hide') { $(element) .removeClass('ng-hide') .css('opacity', 1) .animate( {'opacity': 0}, 1000,
$(element) .addClass('ng-hide') .css('opacity', 1); done(); } ); } else { done(); } } }; }); JSFiddle: http://jsfiddle.net/msfrisbie/4taoda1e/
Note that here, the opacity value is used for the animation, but is not the active class that hides the element. After its use in the animation, it must be reset to 1 in order to not interfere with the subsequent display toggling.
How it works…
Independent of what is defined in the actual class that is being added, ngAnimate provides animation hooks for the class that is being added to define animations. In the context of the ng-show directive, the ng-hide CSS class is defined implicitly within AngularJS, but the animation hooks are completely decoupled from the original class in order to provide a fresh animation definition interface. The following set of tables defines how the addClass animation state machine operates.
The initial state of the animation components is defined as follows:
element <div class="animate-container" ng-show="displayToggle"> Fade me out!
</div>
The following table represents a full addClass animation transition:
Event DOM
The $animate.
addClass(element, 'ng- hide') method is called
<div class="animate-container" ng-show="displayToggle"> Fade me out!
</div>
The $animate service runs the JavaScript-defined animations detected on the element; ng-animate is added
<div class="animate-container ng- animate"
ng-show="displayToggle"> Fade me out!
</div>
The .ng-hide-add class is added to the element <div class="animate-container ng- animate ng-hide-add" ng-show="displayToggle"> Fade me out! </div>
The $animate service waits for a single animation frame (this performs a reflow)
No change in DOM The .ng-hide and .ng-hide-
add-active classes are added (this triggers the CSS transition/animation)
<div class="animate-container ng- animate ng-hide ng-hide-add ng-hide- add-active"
ng-show="displayToggle"> Fade me out!
</div>
The $animate service scans the element styles to get the CSS transition/animation duration and delay
No change in DOM
The $animate service waits for the animation to get completed (via events and timeout)
No change in DOM The animation ends and all the
generated CSS classes are removed from the element
<div class="animate-container ng- hide"
ng-show="displayToggle"> Fade me out!
</div>
The ng-hide class is kept on the
element No change in DOM The doneCallback() callback is
See also
f The Creating removeClass animations with ngClass recipe provides the details of the complementary removeClass event