Skip to content

luisvt/ngts-annotations

 
 

Repository files navigation

ngts-annotations

GitHub version Build Status Coverage Status

npm version

Bower version

TypeScript 1.8+ annotations (decorators) for AngularJS 1.5.x

What ?

ngts-annotations provides annotation like decorators:

@Service()
@Inject(dependencyOne: string, ...dependencies?: string[])
@Controller()
@Directive(config: IDirectiveConfig)
@Factory()
@Resource()
@Component(config: IComponentConfig)

Why ?

Purpose of those decorators is to remove some ugly boilerplate from AngularJS applications written in TypeScript.

How ?

Service

Without annotations we have to:

//# some.service.ts

export class SomeService {

    constructor() {
        // do stuff $http and $parse
    }

    public someMethod(anArg: number): boolean {
        // do some stuff
    }

}

and in the main module:

//# some.module.ts

import {SomeService} from 'some.service.ts';

export someModule = angular.module('someModule', []).service('someService', SomeService);

Using ngts-annotations it will look like:

@Service()
export class SomeService {

    constructor() {
        // do stuff
    }

    public someMethod(anArg: number): boolean {
        // do some stuff
    }

}

and in the main module:

//# some.module.ts

import {SomeService} from 'some.service.ts';

export someModule = autoDeclare('someModule', [], [SomeService]);

Inject

Without annotations we have to:

export class SomeService {
    // this line is error prome doubt to we should keep the same order as in the constructor
    static $inject = ['$http', '$parse'];

    constructor(
        private $http: angular.IHttpService,
        private $parse: angular.IParseService
    ) {
        // do stuff with $http and $parse;
    }

    public someMethod(anArg: number): boolean {
        // do some stuff with this.$parse
    }

}

and in the main module:

//# some.module.ts

import {SomeService} from 'some.service.ts';

export someModule = angular.module('someModule', []).service('someService', SomeService);

Using ngts-annotations it will look like:

@Service()
export class SomeService {

    constructor(
        @Inject('$http') $http: angular.IHttpService,
        @Inject('$parse') private $parse: angular.IParseService
    ) {
        // do stuff with $http and $parse;
    }

    public someMethod(anArg: number): boolean {
        // do some stuff with this.$parse
    }

}

or

@Service()
@Inject('$http', '$parse') // still error prone
export class SomeService {

    constructor(
        private $http: angular.IHttpService,
        private $parse: angular.IParseService
    ) {
        // do stuff with $http and $parse;
    }

    public someMethod(anArg: number): boolean {
        // do some stuff with this.$parse();
    }

}

and in the main module:

//# some.module.ts

import {autoDeclare} from 'ngts-annotations/src/at-angular';
import {SomeService} from 'some.service.ts';

export someModule = autoDeclare('someModule', [], [SomeService]);

Controller

Without annotations we have to:

//# some.controller.ts

export class SomeController {

    static $inject = ['$scope', '$parse'];

    constructor(
        private $scope: IScope,
        private $parse: IParseService
    ) {
        // do stuff with $scope and $$parse;
    }

    public someMethod(anArg: number): boolean {
        // do some stuff with this.$$parse();
    }
}

and in the main module:

//# some.module.ts

import {SomeService} from 'some.service.ts';

export someModule = angular.module('someModule', []).service('someController', SomeController);

Using ngts-annotations it will look like:

@Controller()
export class SomeController {

    constructor(
        @inject('$scope') private $scope: angular.IScope,
        @inject('$parse') private $parse: angular.IParseService
    ) {
        // do stuff with $scope and $$parse;
    }

    public someMethod(anArg: number): boolean {
        // do some stuff with this.$$parse();
    }
}

and in the main module:

//# some.module.ts

import {SomeService} from 'some.service.ts';

export someModule = autoDeclare('someModule', [], [SomeController]);

Directive

Without annotations we have to do:

//# test.dircetive.ts

class TestDirective {

  // And the rest are simple Ctrl instance members
  name: string;

  constructor(private $scope: IFirstComponentScope,
              private $parse: IParseService) {
    $scope.name = this.name = 'FirstTestCtrl';
  }

  setCtrlName(name: string): void {
    this.$parse('name').assign(this, name);
  }
}

export const testDirectiveConfig = {
  name: 'testDirective',
  restrict: 'E',
  link: (scope, element, attrs, ctrl: TestDirective) => {
    element.addClass('test-component');
    ctrl.setCtrlName('FAKE_CTRL_NAME');
  },
  controller: TestDirective
  controllerAs: 'ctrl',
  template: '<span>{{ name }}</span><span>{{ ctrl.name }}</span>'
};

and in the main module:

//# some.module.ts

import {SomeService} from 'some.service.ts';

export someModule = angular.module('someModule', []).service('testDirective', testDirectiveConfig);

Using ngts-annotations it will look like:

@Directive({
  name: 'testDirective',
  restrict: 'E',
  link: (scope, element, attrs, ctrl: TestDirective) => {
    element.addClass('test-component');
    ctrl.setCtrlName('FAKE_CTRL_NAME');
  },
  controllerAs: 'ctrl',
  template: '<span>{{ name }}</span><span>{{ ctrl.name }}</span>'
})
export class TestDirective {

  // And the rest are simple Ctrl instance members
  name: string;

  constructor(@Inject('$scope') private $scope: IFirstComponentScope,
              @Inject('$parse') private $parse: IParseService) {
    $scope.name = this.name = 'FirstTestCtrl';
  }

  setCtrlName(name: string): void {
    this.$parse('name').assign(this, name);
  }
}

and in the main module:

//# some.module.ts

import {TestDirective} from 'test.directive.ts';

export someModule = autoDeclare('someModule', [], [TestDirective]);

Factory

If you use constructors/classes to create common entities a @ClassFactory can be useful. It passes constructor as angular service and attaches $inject's to it's prototype with leading $.

//# test.factory.ts

import {Factory, Inject} from '../src/at-angular';
import IParseService = angular.IParseService;
import IHttpService = angular.IHttpService;

@Factory()
@Inject('$http', '$parse')
export class TestFactory {

  public accept: string;

  $http: IHttpService;
  $parse: IParseService;

  constructor() {
    this.accept = this.$parse('defaults.headers.common.Accept')(this.$http);
  }
}

then somewhere else:

    
    constructor() {
        this.testFactory = new TestFactory();
    }
    

and finally in the main module:

//# some.module.ts

import {TestFactory} from 'test.factory.ts';

export someModule = autoDeclare('someModule', [], [TestFactory]);

Resource

This one is somehow similar to @ClassFactory, but it also encapsulates magic powers of angular $resource. $resource configs are gathered from static class members.

//# user.resource.ts

import {Resource, BaseResource} from "../src/at-angular-resource";
import {Inject} from "../src/at-angular";
import IHttpService = angular.IHttpService;
import IParseService = angular.IParseService;

interface IUser {
  name: string;
  age: number;
}

@Resource()
@Inject('$http', '$parse')
export class UserResource extends BaseResource implements IUser {
  // And to keep proper type, you may add "extends at.Resource"

  static url: string = '/fake/url';

  name: string;
  age: number;

  private $http: IHttpService;
  private $parse: IParseService;

  constructor(model?: IUser) {
    super(model);
    /* istanbul ignore else */
    if (model) {
      this.name = model.name;
      this.age = model.age;
    }
  }

  getLabel(): string {
    return this.$parse('defaults.headers.common.Accept')(this.$http) + this.name + String(this.age);
  }

}

About

TypeScript 1.7 annotations (decorators) for AngularJS 1.x

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 88.3%
  • JavaScript 11.7%