Previous month:
May 2016
Next month:
February 2017

June 2016

The new Angular2 Animation DSL

With one of the next Angular2 release candidate (probably RC2) the new Animation DSL (Domain Specific Language) will be introduced. You will then be able to trigger animations by changing properties of your component's class. The coupling is done in the component's template with the new animation prefix @ in front of the trigger's name followed by an expression which is evaluated during change detection:

<div @myAnimationTrigger="myStatusExp">...</div>

The trigger's name is the identifier of the animation which has to be defined in your component's decorator:

@Component({
  selector: 'my-component',
  templateUrl: 'my-component-tpl.html',
  animations: [
    trigger('myAnimationTrigger', [
      state(...),
      state(...),
      transition(...),
      transition(...)
    ])
  ]
})
class MyComponent {
  myStatusExp = "something";
}

At this point you have to use the new Animation DSL to define and control your animations. The reason for choosing an own DSL here is to be independent from DOM-specific environments and Angular may also do some performance optimizations in the background. The language is not complex and quite easy to understand. Basically, you just need to name your animation trigger, define the states which could be the result of your expression (like "expanded", "collapsed", ...) and write your final visual representation of the target element (on which the animation trigger is set in the template) as an object which looks like writing CSS in each of these states. Of course, this could be even more powerful by defining sequences or groups (or both) and even introducing key frames to explicitly define the transition of the styled properties. After defining the states, you declare the duration of the animation when a state (the result of your expression) changes to another one. At this point it is also possible to introduce some in-between animation before the element animates to its final state. That's the theory, but how does it look? I will show you and explain each line:

animations: [                                         // 1
  trigger('openClose', [                              // 2
    state('collapsed, void',                          // 3
      style({ height: '0px', color: 'lightgreen' })), // 4
    state('expanded',                                 // 5
      style({ height: AUTO_STYLE, color: 'red' })),   // 6
    transition('collapsed <=> expanded', [            // 7
      animate(1000)                                   // 8
    ])                                                // 9
  ])                                                  // 10
]                                                     // 11

On line 1 the new property of the component's decorator is shown. It's type is an array where the triggers are declared.

Line 2 is defining our trigger named openClose. This name is used later in the component's template together with the @ prefix.

Beginning with line 3 the first state is defined. This state will be used when the expression returns collapsed. But wait, there is another interesting value in the first parameter: void. The void state is a reserved name and represents the element's visuals when it is not part of the application (like it would be removed or added by ngIf). With this special state you are now able to animate the removal (and addition, of course) of an element with the same mechanism. In the past you may have used the classes ng-enter and ng-leave together with CSS based animations (while the latter was buggy and only the addition could be animated), but this part is completely gone from Angular's code base (so you need to change your animations with one of the next releases - BREAKING CHANGE). The second parameter of the state method is the final state of the element which should be applied when the state is reached.

The style in line 4 represents the visual appearance of the element in the collapsed state and also when the element is added or removed (because of using void in the state's name parameter).

When the expression returns expanded the state defined on line 5 will be used.

A special constant is used as the value in the styles on line 6 for the property height: AUTO_STYLE (which represents internally a string containing an asterisk). In my opinion, this is one of the most awesome parts of this animation framework. You are now able to use current style property values of the element in your animations. With the example written above, the element expands to its content based height! Isn't this crazy? I think so!

In line 7 the transitions of the state are declared. In this example I am using a two-way transition which is represented by <=>. To define them in one direction => should be used instead. Using this notation, you may define a faster or slower animation if the element collapses or expands. You have the power here. Using an asterisk as one of the sides any state would be used at that position (like transition from any state to void would be written as * => void).

Setting the duration of the animation (from the current visual representation to the final representation) is done in line 8. The first parameter can either be a number (which represents milliseconds) or a string using the CSS based notation for a duration. Even easing (by name or as cubic bezier definition) could be used.

That's all. With these lines of code you have a working expand/collapse animation without writing any line of CSS. Here's the code of a component which is using this animation:

import {AUTO_STYLE, Component, trigger, state, animate, transition, style} from '@angular/core';

@Component({
  selector: 'animation-example',
  styles: [`
    .container {
      background-color: white;
      border: 10px solid black;
      width: 200px;
      text-align: center;
      line-height: 100px;
      font-size: 50px;
      box-sizing: border-box;
      overflow: hidden;
    }
  `],
  animations: [
    trigger('openClose', [
      state('collapsed, void',
        style({ height: '0px', color: 'lightgreen' })),
      state('expanded',
        style({ height: AUTO_STYLE, color: 'red' })),
      transition('collapsed <=> expanded', [
        animate(1000)
      ])
    ])
  ],
  template: `
    <button (click)="expand()">Expand</button>
    <button (click)="collapse()">Collapse</button>
    <hr />
    <div class="container" @openClose="animationState">
      Look at this box
    </div>
  `
})
export class AnimationExampleComponent {
  animationState: string;

  constructor() {
    this.collapse();
  }

  expand() {
    this.animationState = 'expanded';
  }

  collapse() {
    this.animationState = 'collapsed';
  }
}

I'm really looking forward to the availability of this this awesome part of Angular2 and hope the Angular team releases it soon.