Cordova vs. Zone.js or "Why is Angular's document event listener not in a zone?"

If you put an Angular Single Page Application into a Cordova wrapped application any "document:*" host listener (e.g. "document:click") won't be triggered with a zone update. This means Angular is not running the change detection to see if there is any update to be processed in the view etc.

The reason for this is that Cordova is monkey-patching window.addEventListener and document.addEventListener. But wait! Isn't Zone.js also monkey-patching these, too? Yes, indeed. But this is done on EventTarget's prototype directly. Cordova however is assigning a new function to those methods. And here comes the problematic part:

This is breaking the prototype inheritance. Zone.js is not involved anymore! Now, any event listener added won't run in a zone (and trigger the change detection).

But what could you do? Maybe call run of NgZone every time in your callback? This would trigger a useless global change detection run in a non-Cordova environment. Fairly not the best idea. Could we just some kind of restore the previous prototype inheritance way? Yes we can! But we need to break it by ourself first. ;)

(function () {
  'use strict';

  window.addEventListener = function () {
    EventTarget.prototype.addEventListener.apply(this, arguments);
  };

  window.removeEventListener = function () {
    EventTarget.prototype.removeEventListener.apply(this, arguments);
  };

  document.addEventListener = function () {
    EventTarget.prototype.addEventListener.apply(this, arguments);
  };

  document.removeEventListener = function () {
    EventTarget.prototype.removeEventListener.apply(this, arguments);
  };
})();

That's all? Yes! But what is happening here? Just some fancy JavaScript magic. When these methods get called, the method of the EventTarget prototype will be called right in the new implementation. The this context will be switched to window and all provided arguments will be forwarded. And the secret is that this needs to be done before cordova.js is executed. In that case Cordova is monkey-patching our implementation which is redirecting the call to the prototype  (later monkey-patched by Zone.js).

And there is the zone again!


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.


Tired of waiting for TypeScript compilation?

Compiling (or transpiling) TypeScript into JavaScript takes some time. And with many TypeScript files it takes even longer. But what could you do to speed things up?

There is one compilation option you may stumble upon: isolatedModules

By setting this to true, the compilation times will be incredible fast. But why is this happening? The reason is, that the TypeScript compiler doesn't do any type checking anymore with this option switched on. You may ask yourself "Isn't TypeScript also for explicit type checking and why should I switch this off?". Yes that is true, but during development you should not wait for the transpiler over and over. And if you use a modern IDE (e.g. WebStorm, VS Code) the IntelliSense will guide you and show the type problems during writing the code.

A common gulp task for transpiling TypeScript code with sourcemaps for an Angular2 SPA would look like this (I even don't use a project anymore):

gulp.task('build:scripts', () => {
return gulp.src(['./src/app/**/*.ts', './typings/browser.d.ts'], { base: './src' })
.pipe(sourcemaps.init())
.pipe(typescript({
target: 'ES5',
module: 'system',
moduleResolution: 'node',
sourceMap: true,
emitDecoratorMetadata: true,
experimentalDecorators: true,
removeComments: false,
noImplicitAny: false,
isolatedModules: true
}))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('./build'));
});

Finally you should switch this option to false (which is also the default value), of course, when you build a release version of your code to have an explicit last type check by the compiler. Or think about a build server which also does the type checking after you pushed your changes during continuous integration. There are many ways to ensure that the final result is still completely type checked, but during development you should save this time for better things.


Relative template URLs for Angular2 components

When you write an Angular2 Single Page Application you need the TypeScript compiler to create at least ES5 JavaScript code to be able to run your SPA in a modern browser. If you take a closer look at the resulting code generated by the TypeScript compiler with at least version 1.8.2 you should see that there is a local variable called __moduleName which filled with the id property of the second parameter of the inner function:

System.register(['angular2/core'], function(exports_1, context_1) {
    "use strict";
    var __moduleName = context_1 && context_1.id;
    var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
...

Unfortunately, this variable was always undefined. The reason for this could not be found in the TypeScript compiler's code - it is innocent (for this time, boy). But let's take a closer look at the module system Angular is using. Yes! Starting with SystemJS 0.19.19 life came to this variable. With that version (the most recent version is already 0.19.25 by time of writing of this article) the second parameter was finally provided and the variable was now set to the URL of the current module. Like in my sample it contains the following:

http://localhost:8000/app/components/app/app.js

But what can you do with this information? The key to happiness is to take this value and set it on the moduleId property of the @Component decorator:

@Component({
    moduleId: __moduleName,
    selector: 'my-app',
    templateUrl: 'app.html',
...

To satisfy the TypeScript compiler you first need to declare it as a string right at the beginning of our file:

declare var __moduleName : string;

That was easy. You may already spotted that I put a relative URL into the templateUrl property. How could this work? This is now possible as Angular is using the value you set on moduleId to resolve any relative URLs you may use either in templateUrl or even in styleUrls. And by saying "relative URLs" I mean true relative URLs like "../other.html" (but you may not do this, as a component should be together with it's template etc. at one place).

Finally, no more hassle when you move components between folders (but keep watching the import statements). Good guy, SystemJS.