Here is a basic TypeScript/ES.next example that uses decorators for DI and follows the syntax suggested by the framework manual:
import {Component, Inject, Injectable, NgModule, OpaqueToken} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; const CONSTANT = { value: 'constant' }; const CONSTANT_TOKEN = new OpaqueToken; const CONSTANT_PROVIDER = { provide: CONSTANT_TOKEN, useValue: CONSTANT }; @Injectable() class Service { constructor(@Inject(CONSTANT_TOKEN) constant) { console.log('Service constructor', constant); } } @Component({ selector: 'app', template: '...', providers: [Service, CONSTANT_PROVIDER] }) class AppComponent { constructor(@Inject(Service) service: Service, @Inject(CONSTANT_TOKEN) constant) { console.log('AppComponent constructor', service, constant); } } @NgModule({ imports: [BrowserModule], declarations: [AppComponent], bootstrap: [AppComponent] }) class AppModule {} platformBrowserDynamic().bootstrapModule(AppModule);
How would it be written in in ES5?
How would the same thing be done in untranspiled ES6/ES2015?
How are Injectable and Inject decorators translated in these cases?
The question particularly applies to real-world ES6 browser implementations that have classes but may use require or System.import instead of ES6 imports.
To use Angular 2 with ES5 you need this script:
This provides an global variable that contains all of Angular 2. Now you can write ng.core.Component instead of the @Component annotation. The first parameters of the Constructor are the injectables.
var Component = ng.core Component({ selector: 'hello-cmp', template: 'Hello World!', viewProviders: [Service] .Class({ constructor: [Service, function (service) { ... }], });
And tell the injector that our service parameter is a instance of Service
Component.parameters = [[new ng.core.Inject(Service)]];
The following Exapmle shows the usage of angular2 with ES6:
import {Component} from 'angular2/core'; import {Service} from './example.service'; let componentAnnotation = new Component({ selector: 'world-time', inputs: ['timeZones'], providers: [Service], template: ` ... ` }); export class ComponentExample { constructor(service) { this._service = service; } ... } WorldTimeComponent.annotations = [componentAnnotation]; WorldTimeComponent.parameters = [[Service]];
In this plunkr you can find a working ES6 example.
But you can use decorators by using Babel. Enabling the optional[]=es7.decorators (in webpack) or by setting your configuration to stage:1.
Injectable decorator is specific to TypeScript flavour of Angular 2. It enables a class constructor to be implicitly annotated for DI through TypeScript type annotations. It is redundant in TS and unneeded in JS for injected dependencies that are annotated with Inject.
Angular 2 injectables (classes and constructor functions) are supposed to be annotated with annotations and parameters static properties under the hood.
annotations is an array that contains newed decorators for injectable class:
function SomeComponent(...) {} SomeComponent.annotations = [new Componenent(...)];
parameters is an array that contains decorators for constructor parameters, each element is an array that contains a list of newed decorators for respective constructor property (similarly to $inject property explicit annotation in Angular 1.x):
function Service(someService, anotherService) {} Service.parameters = [ [new Inject(SomeService)], [new Inject(AnotherService), new Optional, new SkipSelf] ];
All class decorators are extended from TypeDecorator, meaning that they can be called as functions. In this case so-called DSL syntax is used that allows to chain a decorator with Class helper function:
var SomeComponent = Componenent(...).Class(...);
Class is also available separately, it constructs a new class from given definition object and allows to annotate constructor method with array (similarly to inline array explicit annotation in Angular 1.x):
var SomeService = Class({ constructor: [[new Inject(SomeService)], function (someService) {}] });
Angular 2/4 ES6 with System.import
An example:
Promise.all([ System.import('@angular/core'), System.import('@angular/platform-browser'), System.import('@angular/platform-browser-dynamic') ]) .then(([ {Component, Inject, Injectable, Optional, NgModule, OpaqueToken}, {BrowserModule}, {platformBrowserDynamic} ]) => { const CONSTANT = { value: 'constant' }; const CONSTANT_TOKEN = new OpaqueToken; const CONSTANT_PROVIDER = { provide: CONSTANT_TOKEN, useValue: CONSTANT }; class Service { constructor(constant) {} } Service.parameters = [[new Inject(CONSTANT_TOKEN)]]; class AppComponent { constructor(service, constant) {} } AppComponent.annotations = [new Component({ selector: 'app', template: '...', providers: [Service, CONSTANT_PROVIDER] })]; AppComponent.parameters = [[new Inject(Service)], [new Inject(CONSTANT_TOKEN)]]; class AppModule {} AppModule.annotations = [new NgModule({ imports: [BrowserModule], declarations: [AppComponent], bootstrap: [AppComponent] })]; platformBrowserDynamic().bootstrapModule(AppModule); }) .catch((err) => console.error(err));
Angular 2/4 ES5 with UMD modules and ng global
An example:
var Class = ng.core.Class; var Component = ng.core.Component; var Inject = ng.core.Inject; var Injectable = ng.core.Injectable; var NgModule = ng.core.NgModule; var OpaqueToken = ng.core.OpaqueToken; var BrowserModule = ng.platformBrowser.BrowserModule; var platformBrowserDynamic = ng.platformBrowserDynamic.platformBrowserDynamic; var CONSTANT = { value: 'constant' }; var CONSTANT_TOKEN = new OpaqueToken; var CONSTANT_PROVIDER = { provide: CONSTANT_TOKEN, useValue: CONSTANT }; // Class helper function that uses A1-flavoured inline array DI annotations // and creates an annotated constructor var Service = Class({ constructor: [[new Inject(CONSTANT_TOKEN)], function (constant) { console.log('Service constructor', constant); }] }); // can also be // function Service(constant) {}; // Service.parameters = [[new Inject(...)], ...]; // when not being `new`ed, Component is a chainable factory that has Class helper method var AppComponent = Component({ selector: 'app', template: '...', providers: [Service, CONSTANT_PROVIDER] }) .Class({ constructor: [ [new Inject(Service)], [new Inject(CONSTANT_TOKEN)], function (service, constant) { console.log('AppComponent constructor', service, constant); } ] }); // can also be // function AppComponent(...) {}; // AppComponent.annotations = [new Component(...)]; // AppComponent.parameters = [[new Inject(...)], ...]; var AppModule = NgModule({ imports: [BrowserModule], declarations: [AppComponent], bootstrap: [AppComponent] }) .Class({ constructor: function () {} }); // can also be // function AppModule() {}; // AppModule.annotations = [new NgModule(...)]; platformBrowserDynamic().bootstrapModule(AppModule);