AngularJS Services for Animating Elements

The $animate service allows you to provide transition effects when elements are added, removed, or moved in the DOM. The $animate service doesn’t define any animations itself but relies on the CSS3 animation and transition features. The details of CSS3 animations and transitions are beyond the scope of this book, but I provide a full description in The Definitive Guide to HTML5 book, which is also published by Apress.

Note Unfortunately, the nature of animations makes them impossible to show in static screenshots. To understand how they work, you will need to experience the effects they generate. But you don’t have to retype the code to do this. The examples in this chapter are included in the free source code download that accompanies this book, available from www.apress.com.

1. Why and When to Use the Animation Service

Animations can be a useful means of drawing the user’s attention to an important change in the layout of an application, making the transition from one state to another less jarring.

Many developers treat animations as an outlet for their frustrated artistic ambition and ladle on as many as possible. The results can be annoying, especially for the user who has to endure endless special effects every time they perform a task. For a line-of-business application, where the user could be repeating the same set of actions all day, the effect is demoralizing beyond description.

Animations should be subtle, brief, and quick. The goal is to draw the user’s attention to the fact that something has changed. Use animations consistently, cautiously, and—above all—sparingly.

2. Installing the ngAnimation Module

The $animation service is defined within an optional module called ngAnimate that must be downloaded into the angularjs folder. Go to http://angularjs.org, click Download, select the version you require (version 1.2.5 is the latest version as I write this), and click the Extras link in the bottom-left corner of the window, as shown in Figure 23-1.

Download the angular-animate.js file into the angularjs folder. In Listing 23-1, you can see how I have added a script element for the new file to the products.html file.

Listing 23-1. Adding a Reference to the products.html File

<!DOCTYPE html>

<html ng-app=”exampleApp”>

<head>

<title>Products</title>

<script src=”angular.js”></script>

<script src=”angular-resource.js”></script>

<script src=”angular-route.js”></script>

<script src=”angular-animate.js”></script>

<link href=”bootstrap.css” rel=”stylesheet” />

<link href=”bootstrap-theme.css” rel=”stylesheet” />

<script src=”products.js”></script>

<script src=”increment.js”></script>

</head>

<body ng-controller=”defaultCtrl”>

<div class=”panel panel-primary”>

<h3 class=”panel-heading”>Products</h3>

<div ng-view></div>

</div>

</body>

</html>

In Listing 23-2, you can see the module dependency that I added to the products.js file for ngAnimate.

Listing 23-2. Adding the Module Dependency in the products.js File

angular.module(“exampleApp”, [“increment”, “ngResource”, “ngRoute”, “ngAnimate”])

.constant(“baseUrl”, “http://localhost:5500/products/”)

.factory(“productsResource”, function ($resource, baseUrl) {

return $resource(baseUrl + “:id”, { id: “@id” },

{ create: { method: “POST” }, save: { method: “PUT” } });

})

.config(function ($routeProvider, $locationProvider) {

3. Defining and Applying an Animation

You don’t work directly with the $animate service to apply animations. Instead, you define animations or transitions with CSS, following a special naming convention, and then apply those names as classes to elements, which also have AngularJS directives. The best way to explain is with an example, and Listing 23-3 shows the changes I have made to the products.html file to animate the transition between views.

Listing 23-3. Animating View Transition in the products.html File

<!DOCTYPE html>

<html ng-app=”exampleApp”>

<head>

<title>Products</title>

<script src=”angular.js”></script>

<script src=”angular-resource.js”></script>

<script src=”angular-route.js”></script>

<script src=”angular-animate.js”></script>

<link href=”bootstrap.css” rel=”stylesheet” />

<link href=”bootstrap-theme.css” rel=”stylesheet” />

<script src=”products.js”></script>

<script src=”increment.js”></script>

<style type=”text/css”>

.ngFade.ng-enter { transition: O.ls linear all; opacity: 0; }

.ngFade.ng-enter-active { opacity: 1; }

</style>

</head>

<body ng-controller=”defaultCtrl”>

<div class=”panel panel-primary”>

<h3 class=”panel-heading”>Products</h3>

<div ng-view class=”ngFade”></div>

</div>

</body>

</html>

The key to understand what’s happening in this example is the knowledge that some of the built-in directives support animations when they change their content. Table 23-2 lists directives and the names given to those changes for the purposes of animation.

The name enter is used when content is shown to the user. The name leave is used when content is hidden from the user. The name move is used when content is moved within the DOM. The names add and remove are used when content is added and removed from the DOM.

With Table 23-2 as a reference, you can get a sense of the contents of the style element I added to the example:

<style type=”text/css”>

.ngFade.ng-enter { transition: 0.1s linear all; opacity: 0; }

.ngFade.ng-enter-active { opacity: 1; }

</style>

I have defined two CSS classes, ngFade.ng-enter and ngFade.ng-enter-active, and the names of these classes is important. The first part of the name—ngFade in this case—is the name used to apply the animations or transitions to the element, like this:

<div ng-view class=”ngFade”></div>

Tip There is no requirement to prefix the top-level class name with ng, as I have done, but this is something that I have taken to doing to avoid conflicts with other CSS classes. The transition I have defined in the example causes elements to fade into view, and you might reasonably be tempted to use the name fade. However, Bootstrap, which I am also using in this example, also defines a CSS class fade, and that kind of conflict can cause problems. This has happened to me often enough that I now prefix my AngularJS animation classes with ng, just to make sure that the names are unique within the application.

The second part of the name tells AngularJS what the CSS style is to be used for. There are two names in this example: ng-enter and ng-enter-active. The ng- prefix is required, and AngularJS won’t process the animation without it. The next part of the name corresponds to the details in Table 23-2. I am using the ng-view directive, which will perform animations when a view is displayed to the user and hidden from the user. My styles use the prefix ng-enter, which tells AngularJS that they should be used when a view is shown to the user.

The two styles define the start and end points for the transition that I want the ng-view directive to use. The ng-enter style defines the start point and details of the transition. I have specified that the CSS opacity property is initially 0 (meaning that the view is initially transparent and not visible to the user) and that the transition should be performed over a tenth of a second (I was serious when I said that animations should be brief). The ng-enter-active style defines the end point for the transition. I have specified that the CSS opacity property should be 1, meaning that the view will be entirely opaque and so visible to the user.

The overall effect is that when the view changes, the ng-view directive will apply the CSS classes to the new view, which will transition it from transparent to opaque—basically, fading in the new view.

4. Avoiding the Perils of Parallel Animation

It is natural to assume you have to animate both the departure of old content and the arrival of new content, but doing so can be troublesome. The problem is that under normal circumstances, the ng-view directive adds the new view to the DOM and then removes the old one. If you try to animate the showing of the new content and the hiding of the old, then you will end up with both displayed at once. Listing 23-4 shows additions to the products.html file that will demonstrate the problem.

Listing 23-4. Adding Leave Animations to the products.html File

<!DOCTYPE html>

<html ng-app=”exampleApp”>

<head>

<title>Products</title>

<script src=”angular.js”></script>

<script src=”angular-resource.js”></script>

<script src=”angular-route.js”></script>

<script src=”angular-animate.js”></script>

<link href=”bootstrap.css” rel=”stylesheet” />

<link href=”bootstrap-theme.css” rel=”stylesheet” />

<script src=”products.js”></script>

<script src=”increment.js”></script>

<style type=”text/css”>

.ngFade.ng-enter { transition: 0.1s linear all; opacity: 0; }

.ngFade.ng-enter-active { opacity: 1; }

.ngFade.ng-leave { transition: O.ls linear all; opacity: 1; }

.ngFade.ng-leave-active { opacity: O; }

</style>

</head>

<body ng-controller=”defaultCtrl”>

<div class=”panel panel-primary”>

<h3 class=”panel-heading”>Products</h3>

<div ng-view class=”ngFade”></div>

</div>

</body>

</html>

The result is a brief moment when both views are visible, which is unappealing and confusing to the user. The ng-view directive doesn’t worry about trying to position views over one another, and the new content is just displayed beneath the old, as illustrated in Figure 23-2.

The content is faded because I took the screenshot at the midpoint in the transition and the opacity value of both views is about 0.5. A better effect is achieved by just animating the incoming view using enter. It is subtle, but it makes the view transition less jarring and still draws the user’s attention to the change.

Source: Freeman Adam (2014), Pro AngularJS (Expert’s Voice in Web Development), Apress; 1st ed. edition.

Leave a Reply

Your email address will not be published. Required fields are marked *