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 new
ed 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 new
ed 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);