A-1 AtScript

Angular 2 makes use of a language construct called an 'annotation' - essentially, a quick way to describe some re-used behavior. In order to streamline the transition between Angular 1 and 2, Xing uses a library called A1AtScript to provide annotatin support. It also provides a set of annotations to make Angular 1 code look much more like Angular 2 code.

General Use

Angular Type Annotations

src/app/ExampleStuff.js
import {Controller, Service} from 'a1atscript';

@Controller('ExampleController', ['$scope', 'SomeService'])
export class ExampleController {
  constructor($scope, SomeService) {
  }
}

@Service('ExampleService', ['SomeService'])
export class ExampleService {
    constructor(SomeService) {
    }
}

In general, in Xing projects, we prefer the @Controller syntax over the equivalent angular.module('named').controller syntax. You'll note, however, that there's no reference to the module. Surely it isn't magic, right?

Setting up modules

import {Module} from 'a1atscript';
import {
  ExampleController,
  ExampleService
} from './ExampleStuff.js';

import { AnotherModule } from './anotherA1AtScriptModule.js';

export var MyModule = new Module('MyModule', [
    AnotherModule,
    ExampleController,
    ExampleService,
    'aRegularAngularModule'
]);

Note you can mix other modules, controllers, and services all together in the list -- A1AtScript will figure out how to sort out the module definition.

You can include regular angular modules by just referencing them as strings.

Compile your main app module

src/app/app.js
import {bootstrap, Module} from 'a1atscript';
import { MyModule } from './myModule'

var AppModule = Module('AppModule', [
  MyModule
]);

bootstrap(AppModule, "myAppPrefix");

This is the part where it all comes together. The bootstrap call starts from a root module object (defined in a1atscript) and traverses all of it's dependencies, and all of theirs and so on. Where it finds @Controllers et al, it adds them as items the module provides via Angular DI. Where it finds other modules, it sets the dependencies for Angular and continues to process.

The key thing to know is that, in order for your code to be made available to Angular, it has to be in the dependency list of a module, and that module has to be reachable from your app's root module. It's not difficult, but it can sometimes be surprising when new features don't appear because they haven't been added.

Useful Extras

Shortform notation

If you want to quickly define a module with only one component... just use two annotations

@AsModule('ServiceModule')
@Service('ExampleService')
class ExampleService {
  constructor() {
    this.value = 'Test Value';
  }
}

Get ready for Angular 2!

Angular 2 introduces an entirely new syntax for working with directives. The most common type of directive is a Component. The good news is with A1AtScript you can write components right now, using a syntax remarkably similar to Angular 2.

@Component({
  selector: "awesome",
  properties: { apple: "apple" },
  services: ["ExampleService"]
})
@View({
  templateUrl: "awesome.tpl.html"
})
class AwesomeComponent {
  constructor(exampleService) {
    this.exampleService = exampleService;
    this.test = "test";
  }
  setValue() {
    this.value = this.exampleService.value;
  }
}

There are two annotations in use there: @Component and @View, which is in alignment with the Angular 2 design.

The @Component takes a description object that can have the following fields:

Field name Purpose
selector This is like a CSS selector, but the syntax is a very limited subset. Use awesome to describe an element, .awesome to describe a class-selected component, or [awesome] (equivalent to `restrict: 'E','C','A' in an Angular 1 directive)
properties A list of attributes which will be bound to the component controller. The difference between a simple string and a JS value is at use time - in the above example bind-apple='2+2' would set the value 4, where apple='2+2' would set "2+2".
services Named items to be injected into the component object when it's created

The component object will be made available in the template based on its selector - in essence, the CSS punctuation gets stripped, and Angular normalization is applied to the resulting text. So for instance, all of .example-thing, [example-thing] and example-thing will all get access to the component object as exampleThing, as if controllerAs: exampleThing had been used in an old-style directive.

Use your component like this:

<awesome apple="stringLiteral"></awesome> <!-- awesome.apple == 'stringLiteral' -->
<awesome bind-apple="'stringLiteral'.length"></awesome> <!-- awesome.apple == 13 -->

The @View annotation also takes a description object, which is much simpler:

Field name Purpose
url The url to fetch the template for this component from
inline The literal text of the template for this component

Use one or the other of url or inline in the @View.

If you're more familar with Angular 1.3 syntax, the above @Component example is the equivalent of this:

angular.directive('awesome', function() {
  return {
      restrict: 'E',
      bindToController: {
        apple: "@apple"
        // a setter is created automatically on your
        // controller so that your controller can access this.apple
        ___bindable___apple: "=?bindApple"
      },
      controller, ['ExampleService', AwesomeComponent]
      controllerAs: 'awesome',
      scope: {},
      templateUrl: "awesome.tpl.html",
  }
  function AwesomeComponent(exampleService) {
    this.exampleService = exampleService;
      this.test = "test";
  }
  AwesomeComponent.prototype.setValue = function() {
    this.value = this.exampleService.value
  }
});