36 min read

In this article by Richard Keller, author of the book Learning AngularJS Animations, we will learn how to apply CSS animations within the context of AngularJS by creating animations using CSS transitions and CSS keyframe animations that are integrated with AngularJS native directives using the ngAnimate module. In this article, we will learn:

  • The ngAnimate module setup and usage
  • AngularJS directives with support for out-of-the-box animation
  • AngularJS animations with the CSS transition
  • AngularJS animations with CSS keyframe animations
  • The naming convention of the CSS animation classes
  • Animation of the ngMessage and ngMessages directives

(For more resources related to this topic, see here.)

The ngAnimate module setup and usage

AngularJS is a module-based framework; if we want our AngularJS application to have the animation feature, we need to add the animation module (ngAnimate). We have to include this module in the application by adding the module as a dependency in our AngularJS application.

However, before that, we should include the JavaScript angular-animate.js file in HTML. Both files are available on the Google content distribution network (CDN), Bower, Google Code, and https://angularjs.org/.

The Google developers’ CDN hosts many versions of AngularJS, as listed here:

https://developers.google.com/speed/libraries/devguide#angularjs

Currently, AngularJS Version 1.3 is the latest stable version, so we will use AngularJS Version 1.3.0 on all samples files of this book; we can get them from https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0/angular.min.js and https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0/angular-animate.min.js.

You might want to use Bower. To do so, check out this great video article at https://thinkster.io/egghead/intro-to-bower/, explaining how to use Bower to get AngularJS.

We include the JavaScript files of AngularJS and the ngAnimate module, and then we include the ngAnimate module as a dependency of our app. This is shown in the following sample, using the Google CDN and the minified versions of both files:

<!DOCTYPE html>
<html ng-app"myApp">
<head>
<title>AngularJS animation installation</title>
</head>
<body>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/
   1.3.0/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/
    1.3.0/angular-animate.min.js"></script>
<script>
   var app = angular.module('myApp', ['ngAnimate']);
</script>
</body>
</html>

Here, we already have an AngularJS web app configured to use animations. Now, we will learn how to animate using AngularJS directives.

AngularJS directives with native support for animations

AngularJS has the purpose of changing the way web developers and designers manipulate the Document Object Model (DOM). We don’t directly manipulate the DOM when developing controllers, services, and templates. AngularJS does all the DOM manipulation work for us. The only place where an application touches the DOM is within directives. For most of the DOM manipulation requirements, AngularJS already provides are built-in directives that fit our needs. There are many important AngularJS directives that already have built-in support for animations, and they use the ngAnimate module. This is why this module is so useful; it allows us to use animations within AngularJS directives DOM manipulation. This way, we don’t have to replicate native directives by extending them just to add animation functionality.

The ngAnimate module provides us a way to hook animations in between AngularJS directives execution. It even allows us to hook on custom directives.

As we are dealing with animations between DOM manipulations, we can have animations before and after an element is added to or removed from the DOM, after an element changes (by adding or removing classes), and before and after an element is moved in the DOM. These events are the moments when we might add animations.

Fade animations using AngularJS

Now that we already know how to install a web app with the ngAnimate module enabled, let’s create fade-in and fade-out animations to get started with AngularJS animations.

We will use the same HTML from the installation topic and add a simple controller, just to change an ngShow directive model value and add a CSS transition.

The ngShow directive shows or hides the given element based on the expression provided to the ng-show attribute.

For this sample, we have a Toggle fade button that changes the ngShow model value, so we can see what happens when the element fades in and fades out from the DOM. The ngShow directive shows and hides an element by adding and removing the ng-hide class from the element that contains the directive, shown as follows:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
<title>AngularJS animation installation</title>
</head>
<body>
<style type="text/css">
   .firstSampleAnimation.ng-hide-add,
   .firstSampleAnimation.ng-hide-remove {
    -webkit-transition: 1s ease-in-out opacity;
    transition: 1s ease-in-out opacity;
    opacity: 1;
 }

.firstSampleAnimation.ng-hide {
opacity: 0;
}
</style>
<div>
<div ng-controller=”animationsCtrl”>
<h1>ngShow animation</h1>
<button ng-click=”fadeAnimation =
!fadeAnimation”>Toggle fade</button>
fadeAnimation value: {{fadeAnimation}}
<div class=”firstSampleAnimation” ng-show=”fadeAnimation”>
This element appears when the fadeAnimation model is true
</div>
</div>
</div>
<script src=”//ajax.googleapis.com/ajax/libs/angularjs/
1.3.0/angular.min.js”></script>
<script src=”//ajax.googleapis.com/ajax/libs/angularjs/
1.3.0/angular-animate.min.js”></script>
<script>
var app = angular.module(‘myApp’, [‘ngAnimate’]);
app.controller(‘animationsCtrl’, function ($scope) {
$scope.fadeAnimation = false;
});
</script>
</body>
</html>

In the CSS code, we declared an opacity transition to elements with the firstAnimationSample and ng-hide-add classes, or elements with the firstAnimationSample and ng-hide-remove classes.

We also added the firstAnimationSample class to the same element that has the ng-show directive attribute.

The fadeAnimation model is initially false, so the element with the ngShow directive is initially hidden, as the ngShow directive adds the ng-hide class to the element to set the display property as none.

When we first click on the Toggle fade button, the fadeAnimation model will become true. Then, the ngShow directive will remove the ng-hide class to display the element. But before that, the ngAnimate module knows there is a transition declared for this element. Because of that, the ngAnimate module will append the ng-hide-remove class to trigger the hide animation start.

Then, ngAnimate will add the ng-hide-remove-active class that can contain the final state of the animation to the element and remove the ng-hide class at the same time. Both classes will last until the animation (1 second in this sample) finishes, and then they are removed. This is the fade-in animation; ngAnimate triggers animations by adding and removing the classes that contain the animations; this is why we say that AngularJS animations are class based.

This is where the magic happens. All that we did to create this fade-in animation was declare a CSS transition with the class name, ng-hide-remove. This class name means that it’s appended when the ng-hide class is removed.

The fade-out animation will happen when we click on the Toggle fade button again, and then, the fadeAnimation model will become false. The ngShow directive will add the ng-hide class to remove the element, but before this, the ngAnimate module knows that there is a transition declared for that element too. The ngAnimate module will append the ng-hide-add class and then add the ng-hide and ng-hide-add-active classes to the element at the same time. Both classes will last until the animation (1 second in this sample) finishes, then they are removed, and only the ng-hide class is kept, to hide the element.

The fade-out animation was created by just declaring the CSS transition with the class name of ng-hide-add. It is easy to understand that this class is appended to the element when the ng-hide class is about to be added.

The AngularJS animations convention

As this article is intended to teach you how to create animations with AngularJS, you need to know which directives already have built-in support for AngularJS animations to make our life easier.

Here, we have a table of directives with the directive names and the events of the directive life cycle when animation hooks are supported.

The first row means that the ngRepeat directive supports animation on enter, leave, and move event times.

All events are relative to DOM manipulations, for example, when an element enters or leaves DOM, or when a class is added to or removed from an element.

Directive Supported animations
ngRepeat Enter, leave, and move
ngView Enter and leave
ngInclude Enter and leave
ngSwitch Enter and leave
ngIf Enter and leave
ngClass Add and remove
ngShow and ngHide Add and remove
form and ngModel Add and remove
ngMessages Add and remove
ngMessage Enter and leave

Perhaps, the more experienced AngularJS users have noticed that the most frequently used directives are attended in this list. This is great; it means that animating with AngularJS isn’t hard for most use cases.

AngularJS animation with CSS transitions

We need to know how to bind the CSS animation as well as the AngularJS directives listed in the previous table. The ngIf directive, for example, has support for the enter and leave animations.

When the value of the ngIf model is changed to true, it triggers the animation by adding the ng-enter class to the element just after the ngIf DOM element is created and injected. This triggers the animation, and the classes are kept for the duration of the transition ends. Then, the ng-enter class is removed. When the value of ngIf is changed to false, the ng-leave class is added to the element just before the ngIf content is removed from the DOM, and so, the animation is triggered while the element still exists.

To illustrate the AngularJS ngIf directive and ngAnimate module behavior, let’s see what happens in a sample.

First, we have to declare a button that toggles the value of the fadeAnimation model, and one div tag that uses ng-if=”fadeAnimation”, so we can see what happens when the element is removed and added back.

Here, we create the HTML code using the HTML template we used in the last topic to install the ngAnimate module:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
<title>AngularJS ngIf sample</title>
</head>
<body>
<style>
/* ngIf animation */
.animationIf.ng-enter,
.animationIf.ng-leave {
-webkit-transition: opacity ease-in-out 1s;
transition: opacity ease-in-out 1s;
}
.animationIf.ng-enter,
.animationIf.ng-leave.ng-leave-active {
opacity: 0;
}
.animationIf.ng-leave,
.animationIf.ng-enter.ng-enter-active {
opacity: 1;
}
</style>
<div ng-controller="animationsCtrl">
<h1>ngIf animation</h1>
<div>
fadeAnimation value: {{fadeAnimation}}
</div>
<button ng-click="fadeAnimation = !fadeAnimation">
Toggle fade</button>
<div ng-if="fadeAnimation" class="animationIf">
This element appears when the fadeAnimation model is true
</div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular-animate.min.js"></script>
<script>
var app = angular.module('myApp', ['ngAnimate']);
app.controller('animationsCtrl', function ($scope) {
$scope.fadeAnimation = false;
});
</script>
</body>
</html>

So, let’s see what happens in the DOM just after we click on the Toggle fade button. We will use Chrome Developer Tools (Chrome DevTools) to check the HTML in each animation step. It’s a native tool that comes with the Chrome browser. To open Chrome DevTools, you just need to right-click on any part of the page and click on Inspect Element.

The ng-enter class

Our CSS declaration added an animation to the element with the animationIf and ng-enter classes. So, the transition is applied when the element has the ng-enter class too. This class is appended to the element when the element has just entered the DOM. It’s important to add the specific class of the element you want to animate in the selector, which in this case is the animationIf class, because many other elements might trigger animation and add the ng-enter class too. We should be careful to use the specific target element class.

Until the animation is completed, the resulting HTML fragment will be as follows:

Learning AngularJS Animations

Consider the following snippet:

<div ng-if="fadeAnimation" class="animationIf ng-scope
ng-animate ng-enter ng-enter-active">
fadeAnimation value: true
</div>

We can see that the ng-animate, ng-enter, and ng-enter-active classes were added to the element.

After the animation is completed, the DOM will have the animation classes removed as the next screenshot shows:

Learning AngularJS Animations

As you can see, the animation classes are removed:

<div ng-if="fadeAnimation" class="animationIf ng-scope">
This element appears when the fadeAnimation model is true
</div>

The ng-leave class

We added the same transition of the ng-enter class to the element with the animationIf and ng-leave classes. The ng-leave class is added to the element before the element leaves the DOM. So, before the element vanishes, it will display the fade effect too.

If we click again on the Toggle fade button, the leave animation will be displayed and the following HTML fragment and screen will be rendered:

Learning AngularJS Animations

The fragment rendered is as follows:

<div ng-if="fadeAnimation" class="animationIf ng-scope
g-animate ng-leave ng-leave-active">
This element appears when the fadeAnimation model is true
</div>

We can notice that the ng-animate, ng-leave, and ng-leave-active classes were added to the element.

Finally, after the element is removed from the DOM, the rendered result will be as follows:

Learning AngularJS Animations

The code after removing the element is as follows:

<div ng-controller="animationsCtrl" class="ng-scope">
<div class="ng-binding">
fadeAnimation value: false
</div>
<button ng-click="fadeAnimation = !fadeAnimation">
Toggle fade</button>
<!-- ngIf: fadeAnimation -->
</div>

Furthermore, there are the ng-enter-active and ng-leave-active classes. They are appended to the element classes too. Both are used to define the target value of the transition, and the -active classes define the destination CSS so that we can create a transition between the start and the end of an event. For example, ng-enter is the initial class of the enter event and ng-enter-active is the final class of the enter event. They are used to determine the style applied at the start of the animation beginning and the final transition style, and they are displayed when the transition completes the cycle. A use case of the -active class is when we want to set an initial color and a final color using the CSS transition.

In the last sample case, the ng-leave class has opacity set to 1 and the ng-leave-active class has the opacity set to 0; so, the element will fade away at the end of the animation.

Great, we just created our first animation using AngularJS and CSS transitions.

AngularJS animation with CSS keyframe animations

We created an animation using the ngIf directive and CSS transitions. Now we are going to create an animation using ngRepeat and CSS animations (keyframes).

As we saw in the earlier table on directives and the supported animation events, the ngRepeat directive supports animation on the enter, leave, and move events. We already used the enter and leave events in the last sample. The move event is triggered when an item is moved around on the list of items.

For this sample, we will create three functions on the controller scope: one to add elements to the list in order to execute the enter event, one to remove an item from list in order to execute the leave event, and one to sort the elements so that we can see the move event.

Here is the JavaScript with the functions; $scope.items is the array that we will use on the ngRepeat directive:

var app = angular.module('myApp', ['ngAnimate']);
app.controller('animationsCtrl', function ($scope) {
$scope.items = [{ name: 'Richard' }, { name: 'Bruno' }
, { name: 'Jobson' }];
$scope.counter = 0;
$scope.addItem = function () {
var name = 'Item' + $scope.counter++;
$scope.items.push({ name: name });
};
$scope.removeItem = function () {
var length = $scope.items.length;
var indexRemoved = Math.floor(Math.random() * length);
$scope.items.splice(indexRemoved, 1);
};
$scope.sortItems = function () {
$scope.items.sort(function (a, b) { return a[name]
< b[name] ? -1 : 1 });
};
});

The HTML is as follows; it is without the CSS styles because we will see them later separating each animation block:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
<title>AngularJS ngRepeat sample</title>
</head>
<body>
<div ng-controller="animationsCtrl">
<h1>ngRepeat Animation</h1>
<div>
<div ng-repeat="item in items" class="repeatItem">
{{item.name}}
</div>
<button ng-click="addItem()">Add item</button>
<button ng-click="removeItem()">Remove
item</button><button ng-click="sortItems()">
Sort items</button>
</div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular-animate.min.js"></script>
</body>
</html>

We will add an animation to the element with the repeatItem and ng-enter classes, and we will declare the from and to keyframes. So, when an element appears, it starts with opacity set to 0 and color set as red and will animate for 1 second until opacity is 1 and color is black. This will be seen when an item is added to the ngRepeat array.

The enter animation definition is declared as follows:

/* ngRepeat ng-enter animation */
.repeatItem.ng-enter {
-webkit-animation: 1s ng-enter-repeat-animation;
animation: 1s ng-enter-repeat-animation;
}
@-webkit-keyframes ng-enter-repeat-animation {
from {
opacity: 0;
color: red;
}
to {
opacity: 1;
color: black;
}
}
@keyframes ng-enter-repeat-animation {
from {
opacity: 0;
color: red;
}
to {
opacity: 1;
color: black;
}
}

The move animation is declared next is to be triggered when we move an item of ngRepeat. We will add a keyframe animation to the element with the repeatItem and ng-move classes. We will declare the from and to keyframes. So, when an element moves, it starts with opacity set to 0 and color set as black and will animate for 1 second until opacity is 0.5 and color is blue, shown as follows:

/* ngRepeat ng-move animation */
.repeatItem.ng-move {
-webkit-animation: 1s ng-move-repeat-animation;
animation: 1s ng-move-repeat-animation;
}
@-webkit-keyframes ng-move-repeat-animation {
from {
opacity: 1;
color: black;
}
to {
opacity: 0.5;
color: blue;
}
}
@keyframes ng-move-repeat-animation {
from {
opacity: 1;
color: black;
}
to {
opacity: 0.5;
color: blue;
}
}

The leave animation is declared next and is to be triggered when we remove an item of ngRepeat. We will add a keyframe animation to the element with the repeatItem and ng-leave classes; we will declare the from and to keyframes; so, when an element leaves the DOM, it starts with opacity set to 1 and color set as black and animates for 1 second until opacity is 0 and color is red, shown as follows:

/* ngRepeat ng-leave animation */
.repeatItem.ng-leave {
-webkit-animation: 1s ng-leave-repeat-animation;
animation: 1s ng-leave-repeat-animation;
}
@-webkit-keyframes ng-leave-repeat-animation {
from {
opacity: 1;
color: black;
}
to {
opacity: 0;
color: red;
}
}
@keyframes ng-leave-repeat-animation {
from {
opacity: 1;
color: black;
}
to {
opacity: 0;
color: red;
}
}

We can see that the ng-enter-active and ng-leave-active classes aren’t used on this sample, as the keyframe animation already determines the initial and final properties’ states. In this case, as we used CSS keyframes, the classes with the -active suffix are useless, although for CSS transitions, it’s useful to set an animation destination.

The CSS naming convention

In the last few sections, we saw how to create animations using AngularJS, CSS transitions, and CSS keyframe animations. Creating animations using both CSS transitions and CSS animations is very similar because all animations in AngularJS are class based, and AngularJS animations have a well-defined class name pattern.

We must follow the CSS naming convention by adding a specific class to the directive element so that we can determine the element animation. Otherwise, the ngAnimate module will not be able to recognize which element the animation applies to.

We already know that both ngIf and ngRepeat use the ng-enter, ng-enter-active, ng-leave, and ng-leave-active classes that are added to the element in the enter and leave events. It’s the same naming convention used by the ngInclude, ngSwitch, ngMessage, and ngView directives.

The ngHide and ngShow directives follow a different convention. They add the ng-hide-add and ng-hide-add-active classes when the element is going to be hidden. When the element is going to be shown, they add the ng-hide-remove and ng-hide-remove-active classes. These class names are more intuitive for the purpose of hiding and showing elements. There is also the ngClass directive convention that uses the class name added to create the animation classes with the -add, -add-active, -remove, and -remove-active suffixes, similar to the ngHide directive.

The ngRepeat directive uses the ng-move and ng-move-active classes when elements move their position in the DOM, as we already saw in the last sample.

The ngClass directive animation sample

The ngClass directive allows us to dynamically set CSS classes. So, we can programmatically add and remove CSS from DOM elements. Classes are already used to change element styles, so it’s very good to see how useful animating the ngClass directive is.

Let’s see a sample of ngClass so that it’s easier to understand.

We will create the HTML code with a Toggle ngClassbutton that will add and remove the animationClass class from the element with the initialClass class through the ngClass directive:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
<title>AngularJS ngClass sample</title>
</head>
<body>
<link href="ngClassSample.css" rel="stylesheet" />
<div>
<h1>ngClass Animation</h1>
<div>
<button ng-click="toggleNgClass = !toggleNgClass">Toggle
ngClass</button>
<div class="initialClass" ng-class="
{'animationClass' : toggleNgClass}">
This element has class 'initialClass' and
the ngClass directive is declared as
ng-class="{'animationClass' : toggleNgClass}"
</div>
</div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular-animate.min.js"></script>
<script>
var app = angular.module('myApp', ['ngAnimate']);
</script>
</body>
</html>

For this sample, we will use two basic classes: an initial class and the class that the ngClass directive will add to and remove from the element:

/* ngclass animation */
/*This is the initialClass, that keeps in the element*/
.initialClass {
background-color: white;
color: black;
border: 1px solid black;
}
/* This is the animationClass, that is added or removed by the ngClass
expression*/
.animationClass {
background-color: black;
color: white;
border: 1px solid white;
}

To create the animation, we will define a CSS animation using keyframes; so, we only will need to use the animationClass-add and animationClass-remove classes to add animations:

@-webkit-keyframes ng-class-animation {
from {
background-color: white;
color:black;
border: 1px solid black;
}
to {
background-color: black;
color: white;
border: 1px solid white;
}
}
@keyframes ng-class-animation {
from {
background-color: white;
color:black;
border: 1px solid black;
}
to {
background-color: black;
color: white;
border: 1px solid white;
}
}

The initial state is shown as follows:

Learning AngularJS Animations

So, we want to display an animation when animationClass is added to the element with the initialClass class by the ngClass directive. This way, our animation selector will be:

.initialClass.animationClass-add{
-webkit-animation: 1s ng-class-animation;
animation: 1s ng-class-animation;
}

After 500 ms, the result should be a complete gray div tag because the text, border, and background colors are halfway through the transition between black and white, as we can see in this screenshot:

Learning AngularJS Animations

After a second of animation, this is the result:

Learning AngularJS Animations

The remove animation, which occurs when animationClass is removed, is similar to the enter animation. However, this animation should be the reverse of the enter animation, and so, the CSS selector of the animation will be:

initialClass.animationClass-remove {
-webkit-animation: 1s ng-class-animation reverse;
animation: 1s ng-class-animation reverse;
}

The animation result will be the same as we saw in previous screenshots, but in the reverse order.

The ngHide and ngShow animation sample

Let’s see one sample of the ngHide animation, which is the directive that shows and hides the given HTML code based on an expression, such as the ngShow directive. We will use this directive to create a success notification message that fades in and out.

To have a lean CSS file in this sample, we will use the Bootstrap CSS library, which is a great library to use with AngularJS. There is an AngularJS version of this library created by the Angular UI team, available at http://angular-ui.github.io/bootstrap/.

The Twitter Bootstrap library is available at http://getbootstrap.com/.

For this sample, we will use the Microsoft CDN; you can check out the Microsoft CDN libraries at http://www.asp.net/ajax/cdn.

Consider the following HTML:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
<title>AngularJS ngHide sample</title>
</head>
<body>
<link href="http://ajax.aspnetcdn.com/ajax/bootstrap/3.2.0/css/
bootstrap.css" rel="stylesheet" />
<style>
/* ngHide animation */
.ngHideSample {
padding: 10px;
}
.ngHideSample.ng-hide-add {
-webkit-transition: all linear 0.3s;
-moz-transition: all linear 0.3s;
-ms-transition: all linear 0.3s;
-o-transition: all linear 0.3s;
opacity: 1;
}
.ngHideSample.ng-hide-add-active {
opacity: 0;
}
.ngHideSample.ng-hide-remove {
-webkit-transition: all linear 0.3s;
-moz-transition: all linear 0.3s;
-ms-transition: all linear 0.3s;
-o-transition: all linear 0.3s;
opacity: 0;
}
.ngHideSample.ng-hide-remove-active {
opacity: 1;
}
</style>
<div>
<h1>ngHide animation</h1>
<div>
<button ng-click="disabled = !disabled">Toggle ngHide
animation</button>
<div ng-hide="disabled" class="ngHideSample bg-success">
This element has the ng-hide directive.
</div>
</div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular-animate.min.js"></script>
<script>
var app = angular.module('myApp', ['ngAnimate']);
</script>
</body>
</html>

In this sample, we created an animation in which when the element is going to hide, its opacity is transitioned until it’s set to 0. Also, when the element appears again, its opacity transitions back to 1 as we can see in the sequence of the following sequence of screenshots.

In the initial state, the output is as follows:

Learning AngularJS Animations

After we click on the button, the notification message starts to fade:

Learning AngularJS Animations

After the add (ng-hide-add) animation has completed, the output is as follows:

Learning AngularJS Animations

Then, if we toggle again, we will see the success message fading in:

Learning AngularJS Animations

After the animation has completed, it returns to the initial state:

Learning AngularJS Animations

The ngShow directive uses the same convention; the only difference is that each directive has the opposite behavior for the model value. When the model is true, ngShow removes the ng-hide class and ngHide adds the ng-hide class, as we saw in the first sample of this article.

The ngModel directive and form animations

We can easily animate form controls such as input, select, and textarea on ngModel changes. Form controls already work with validation CSS classes such as ng-valid, ng-invalid, ng-dirty, and ng-pristine. These classes are appended to form controls by AngularJS, based on validations and the current form control status. We are able to animate on the add and remove features of those classes.

So, let’s see an example of how to change the input color to red when a field becomes invalid. This helps users to check for errors while filling in the form before it is submitted. The animation eases the validation error experience. For this sample, a valid input will contain only digits and will become invalid once a character is entered.

Consider the following HTML:

<h1>ngModel and form animation</h1>
<div>
<form>
<input ng-model="ngModelSample" ng-pattern="/^d+$/"
class="inputSample" />
</form>
</div>

This ng-pattern directive validates using the regular expression if the model ngModelSample is a number. So, if we want to warn the user when the input is invalid, we will set the input text color to red using a CSS transition.

Consider the following CSS:

/* ngModel animation */
.inputSample.ng-invalid-add {
-webkit-transition: 1s linear all;
transition: 1s linear all;
color: black;
}
.inputSample.ng-invalid {
color: red;
}
.inputSample.ng-invalid-add-active {
color: red;
}

We followed the same pattern as ngClass. So, when the ng-invalid class is added, it will append the ng-invalid-add class and the transition will change the text color to red in a second; it will then continue to be red, as we have defined the ng-invalid color as red too. The test is easy; we just need to type in one non-numeric character on the input and it will display the animation.

The ngMessage and ngMessages directive animations

Both the ngMessage and ngMessages directives are complimentary, but you can choose which one you want to animate, or even animate both of them. They became separated from the core module, so we have to add the ngMessages module as a dependency of our AngularJS application.

These directives were added to AngularJS in Version 1.3, and they are useful to display messages based on the state of the model of a form control. So, we can easily display a custom message if an input has a specific validation error, for example, when the input is required but is not filled in yet. Without these directives, we would rely on JavaScript code and/or complex ngIf statements to accomplish the same result.

For this sample, we will create three different error messages for three different validations of a password field, as described in the following HTML:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
<title>ngMessages animation</title>
</head>
<body>
<link href="ngMessageAnimation.css" rel="stylesheet" />
<h1>ngMessage and ngMessages animation</h1>
<div>
<form name="messageAnimationForm">
<label for="modelSample">Password validation input</label>
<div>
<input ng-model="ngModelSample" id="modelSample"
name="modelSample" type="password" ng-pattern=
"/^d+$/" ng-minlength="5" ng-maxlength="10"
required class="ngMessageSample" />
<div ng-messages="messageAnimationForm.
modelSample.$error" class="ngMessagesClass"
ng-messages-multiple>
<div ng-message="pattern" class="ngMessageClass">*
This field is invalid, only numbers are allowed</div>
<div ng-message="minlength" class="ngMessageClass">*
It's mandatory at least 5 characters</div>
<div ng-message="maxlength" class="ngMessageClass">*
It's mandatory at most 10 characters</div>
</div>
</div>
</form>
</div>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular-animate.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular-messages.min.js"></script>
<script>
var app = angular.module('myApp', ['ngAnimate',
'ngMessages']);
</script>
</body>
</html>

We included the ngMessage file too, as it’s required for this sample.

For the ngMessages directive, that is, the container of the ngMessage directives, we included an animation on ng-active-addthat changes the container background color from white to red and ng-inactive-add that does the opposite, changing the background color from red to white.

This works because the ngMessages directive appends the ng-active class when there is any message to be displayed. When there is no message, it appends the ng-inactive class to the element. Let’s see the ngMessages animation’s declaration:

.ngMessagesClass {
height: 50px;
width: 350px;
}
.ngMessagesClass.ng-active-add {
transition: 0.3s linear all;
background-color: red;
}
.ngMessagesClass.ng-active {
background-color: red;
}
.ngMessagesClass.ng-inactive-add {
transition: 0.3s linear all;
background-color: white;
}
.ngMessagesClass.ng-inactive {
background-color: white;
}

For the ngMessage directive, which contains a message, we created an animation that changes the color of the error message from transparent to white when the message enters the DOM, and changes the color from white to transparent when the message leaves DOM, shown as follows:

.ngMessageClass {
color: white;
}
.ngMessageClass.ng-enter {
transition: 0.3s linear all;
color: transparent;
}
.ngMessageClass.ng-enter-active {
color: white;
}
.ngMessageClass.ng-leave {
transition: 0.3s linear all;
color: white;
}
.ngMessageClass.ng-leave-active {
color: transparent;
}

This sample illustrates two animations for two directives that are related to each other.

The initial result, before we add a password, is as follows:

Learning AngularJS Animations

We can see both animations being triggered when we type in the a character, for example, in the password input.

Between 0 and 300 ms of the animation, we will see both the background and text appearing for two validation messages:

Learning AngularJS Animations

After 300 ms, the animation has completed, and the output is as follows:

Learning AngularJS Animations

The ngView directive animation

The ngView directive is used to add a template to the main layout. It has support for animation, for both enter and leave events. It’s nice to have an animation for ngView, so the user has a better notion that we are switching views. For this directive sample, we need to add the ngRoute JavaScript file to the HTML and the ngRoute module as a dependency of our app.

We will create a sample that slides the content of the current view to the left, and the new view appears sliding from the right to the left too so that we can see the current view leaving and the next view appearing.

Consider the following HTML:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
<title>AngularJS ngView sample</title>
</head>
<body>
<style>
.ngViewRelative {
position: relative;
height: 300px;
}
.ngViewContainer {
position: absolute;
width: 500px;
display: block;
}
.ngViewContainer.ng-enter,
.ngViewContainer.ng-leave {
-webkit-transition: 600ms linear all;
transition: 600ms linear all;
}
.ngViewContainer.ng-enter {
transform: translateX(500px);
}
.ngViewContainer.ng-enter-active {
transform: translateX(0px);
}
.ngViewContainer.ng-leave {
transform: translateX(0px);
}
.ngViewContainer.ng-leave-active {
transform: translateX(-1000px);
}
</style>
<h1>ngView sample</h1>
<div class="ngViewRelative">
<a href="#/First">First page</a>
<a href="#/Second">Second page</a>
<div ng-view class="ngViewContainer">
</div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular-animate.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular-route.min.js"></script>
<script>
var app = angular.module('myApp', ['ngAnimate', 'ngRoute']);
app.config(['$routeProvider',
function ($routeProvider) {
$routeProvider
.when('/First', {
templateUrl: 'first.html'
})
.when('/Second', {
templateUrl: 'second.html'
})
.otherwise({
redirectTo: '/First'
});
}]);
</script>
</body>
</html>

We need to configure the routes on config, as the JavaScript shows us. We then create the two HTML templates on the same directory. The content of the templates are just plain lorem ipsum.

The first.html file content is shown as follows:

<div>
<h2>First page</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Cras consectetur dui nunc, vel feugiat lectus imperdiet et.
In hac habitasse platea dictumst. In rutrum malesuada justo,
sed porttitor dolor rutrum eu. Sed condimentum tempus est at
euismod. Donec in faucibus urna. Fusce fermentum in mauris
at pretium. Aenean ut orci nunc. Nulla id velit interdum
nibh feugiat ultricies eu fermentum dolor. Pellentesque
lobortis rhoncus nisi, imperdiet viverra leo ullamcorper
sed. Donec condimentum tincidunt mollis. Curabitur lorem
nibh, mattis non euismod quis, pharetra eu nibh.
</p>
</div>

The second.html file content is shown as follows:

<div>
<h2>Second page</h2>
<p>
Ut eu metus vel ipsum tristique fringilla. Proin hendrerit
augue quis nisl pellentesque posuere. Aliquam sollicitudin
ligula elit, sit amet placerat augue pulvinar eget. Aliquam
bibendum pulvinar nisi, quis commodo lorem volutpat in.
Donec et felis sit amet mauris venenatis feugiat non id
metus. Fusce leo elit, egestas non turpis sed, tincidunt
consequat tellus. Fusce quis auctor neque, a ultricies urna.
Cras varius purus id sagittis luctus. Sed id lectus
tristique, euismod ipsum ut, congue augue.
</p>
</div>

Great, we now have our app set up to enable ngView and routes. The animation was defined by adding animation to the enter and leave events, using translateX().

This animation is defined to the new view coming from 500 px from the right and animating until the position on the x-axis is 0, leaving the view in the left corner. The leaving view goes from the initial position until it is at -1000 px on the x-axis. Then, it leaves the DOM. This animation creates a sliding effect; the leaving view leaves faster as it has to move the double of the distance of the entering view in the same animation duration.

We can change the translation using the y-axis to change the animation direction, creating the same sliding effect but with different aesthetics.

The ngSwitch directive animation

The ngSwitch directive is a directive that is used to conditionally swap the DOM structure based on an expression. It supports animation on the enter and leave events, for example, the ngView directive animation events.

For this sample, we will create the same sliding effect of the ngView sample, but in this case, we will create a sliding effect from top to bottom instead of right to left. This animation helps the user to understand that one item is being replaced by the other.

The ngSwitch sample HTML is shown as follows:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
<title>AngularJS ngSwitch sample</title>
</head>
<body>
<div ng-controller="animationsCtrl">
<h1>ngSwitch sample</h1>
<p>Choose an item:</p>
<select ng-model="ngSwitchSelected" ng-options="item for item
in ngSwitchItems"></select>
<p>Selected item:</p>
<div class="switchItemRelative" ng-switch
on="ngSwitchSelected">
<div class="switchItem" ng-switch-when="item1">Item
1</div>
<div class="switchItem" ng-switch-when="item2">Item
2</div>
<div class="switchItem" ng-switch-when="item3">Item
3</div>
<div class="switchItem" ng-switch-default>Default
Item</div>
</div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular-animate.min.js"></script>
<script>
var app = angular.module('myApp', ['ngAnimate']);
app.controller('animationsCtrl', function ($scope) {
$scope.ngSwitchItems = ['item1', 'item2', 'item3'];
});
</script>
</body>
</html>

In the JavaScript controller, we added the ngSwitchItems array to the scope, and the animation CSS is defined as follows:

/* ngSwitch animation */
.switchItemRelative {
position: relative;
height: 25px;
overflow: hidden;
}
.switchItem {
position: absolute;
width: 500px;
display: block;
}
/*The transition is added when the switch item is about to
enter or about to leave DOM*/
.switchItem.ng-enter,
.switchItem.ng-leave {
-webkit-transition: 300ms linear all;
-moz-transition: 300ms linear all;
-ms-transition: 300ms linear all;
-o-transition: 300ms linear all;
transition: 300ms linear all;
}
/* When the element is about to enter DOM*/
.switchItem.ng-enter {
bottom: 100%;
}
/* When the element completes the enter transition */
.switchItem.ng-enter-active {
bottom: 0;
}
/* When the element is about to leave DOM*/
.switchItem.ng-leave {
bottom: 0;
}
/*When the element end the leave transition*/
.switchItem.ng-leave-active {
bottom: -100%;
}

This is almost the same CSS as the ngView sample; we just used the bottom property, added a different height to the switchItemRelative class, and included overflow:hidden.

The ngInclude directive sample

The ngInclude directive is used to fetch, compile, and include an HTML fragment; it supports animations for the enter and leave events, such as the ngView and ngSwitch directives. For this sample, we will use both templates created in the last ngView sample, first.html and second.html.

The ngInclude animation sample HTML with JavaScript and CSS included is shown as follows:

<!DOCTYPE html>
<html ng-app="myApp">
<head>
<title>AngularJS ngInclude sample</title>
</head>
<body>
<style>
.ngIncludeRelative {
position: relative;
height: 500px;
overflow: hidden;
}
.ngIncludeItem {
position: absolute;
width: 500px;
display: block;
}
.ngIncludeItem.ng-enter,
.ngIncludeItem.ng-leave {
-webkit-transition: 300ms linear all;
transition: 300ms linear all;
}
.ngIncludeItem.ng-enter {
top: 100%;
}
.ngIncludeItem.ng-enter-active {
top: 0;
}
.ngIncludeItem.ng-leave {
top: 0;
}
.ngIncludeItem.ng-leave-active {
top: -100%;
}
</style>
<div ng-controller="animationsCtrl">
<h1>ngInclude sample</h1>
<p>Choose one template</p>
<select ng-model="ngIncludeSelected" ng-options="item.name for
item in ngIncludeTemplates"></select>
<p>ngInclude:</p>
<div class="ngIncludeRelative">
<div class="ngIncludeItem" nginclude="
ngIncludeSelected.url"></div>
</div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs
/1.3.0/angular-animate.min.js"></script>
<script>
var app = angular.module('myApp', ['ngAnimate']);
app.controller('animationsCtrl', function ($scope) {
$scope.ngIncludeTemplates = [{ name: 'first', url:
'first.html' }, { name: 'second', url: 'second.html' }];
})
</script>
</body>
</html>

In the JavaScript controller, we included the templates array.

Finally, we can animate ngInclude using CSS. In this sample, we will animate by sliding the templates using the top property, using the enter and leave events animation. To test this sample, just change the template value selected.

Do it yourself exercises

The following are some exercises that you can refer to as an exercise that will help you understand the concepts of this article better:

  1. Create a spinning loading animation, using the ngShow or ngHide directives that appears when the scope controller variable, $scope.isLoading, is equal to true.
  2. Using exercise 1, create a gray background layer with opacity 0.5 that smoothly fills the entire page behind the loading spin, and after page content is loaded, covers all the content until isProcessing becomes false. The effect should be that of a drop of ink that is dropped on a piece of paper and spreads until it’s completely stained.
  3. Create a success notification animation, similar to the ngShow example, but instead of using the fade animation, use a slide-down animation. So, the success message starts with height:0px. Check http://api.jquery.com/slidedown/ for the expected animation effect.
  4. Copy any animation from the http://capptivate.co/ website, using AngularJS and CSS animations.

Summary

In this article, we learned how to animate AngularJS native directives using the CSS transitions and CSS keyframe concepts. This article taught you how to create animations on AngularJS web apps.

Resources for Article:


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here