Angular 2 dependency injection in ES5 and ES6
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) {}]
});
Class
helper was deprecated in latest framework versions. It is supposed to be replaced with raw functions or third-party class helpers in ES5. Decorators support direct chaining with class functions, Componenent(...)(ComponentClass)
.
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);
Angular 5 : How to inject Router service with ES6/ES5 (without Typescript)
Injectable
is for TypeScript only, it uses emitted parameter types to annotate injectable class for DI.
Classes should be annotated with static parameter
property in ES5 and ES6, as shown in this answer:
class HomeComponent {
constructor(service, router) {
this.message = service.toUpperCase('hello');
}
}
HomeComponent.parameters = [
[new ng.core.Inject(Service)],
[new ng.core.Inject(ng.router.Route)]
];
A problem with this example is that Router
should be global provider, it shouldn't be defined in component providers
. Here's an example.
create an angular 2 service in es5
Here is a complete sample of dependency injection with ES5. (service into component, service into service). Don't forget to specify your service when bootstrapping your application or within the providers
attribute of components.
var OtherService = function() {};
OtherService.prototype.test = function() {
return 'hello';
};
var Service = ng.core.Injectable().Class({
constructor: [ OtherService, function (service) {
this.service = service;
}],
test: function() {
return this.service.test();
}
});
var AppComponent = ng.core
.Component({
selector: 'my-app',
template: '<div>Test: {{message}}</div>',
})
.Class({
constructor: [Service, function (service) {
this.message = service.test();
}],
});
document.addEventListener('DOMContentLoaded', function () {
ng.platform.browser.bootstrap(AppComponent, [
OtherService, Service
]);
});
In your case, I think that your forgot to add app.database
in providers. Something like:
document.addEventListener('DOMContentLoaded', function () {
ng.platform.browser.bootstrap(AppComponent, [
app.database
]);
});
You could also have a look at this question:
- Dependency Injection in Angular 2 with ES5
Migration to angular 2 - ES6 or TypeScript?
Typescript isn't really about OOP. OOP is orthogonal to types (think Java vs. Smalltalk). Typescript is about static type verification: are you using a string where you think you're using an array? I actually fixed a bug in a co-worker's code along those lines just last Friday where he was using a for
loop over the length of what he thought was an array (Array.prototype.forEach makes that an easy to find error).
Is static type-checking worth the effort of adding type annotations all over your codebase? That's a judgement call.
ES6/ESNext on the other hand just flat-out offers you better ways to write code. I don't see how anyone can make the argument that
function(arr) {
var foo = arr[0];
var bar = arr[1];
return foo + bar;
}
is better than
([foo, bar]) => foo + bar
Same for lots of other features, if you write in a functional style your codebase (like mine) is probably littered with
Object.keys(someObj)
.map(k => someObj[k])
.filter...
Now you just have Object.values
. Plus about a million other features.
How to use angular2 DynamicComponentLoader in ES6?
If you want to write code in ES7, I think the most concise approach to specify injections at this time is to use static getter for parameters
:
import {Component, View, DynamicComponentLoader, ElementRef } from 'angular2/angular2'
@Component({
selector: 'my-app'
})
@View({
template: '<div #container></b>'
})
export class App {
static get parameters() {
return [[DynamicComponentLoader], [ElementRef]];
}
constructor(dynamicComponentLoader, elementRef) {
dynamicComponentLoader.loadIntoLocation(DynamicComponent, elementRef, 'container');
}
}
See this plunker
If you want to write code in ES6, which doesn't support decorators, you must also use static getter for annotations
property. In this case you must import ComponentMetadata
and ViewMetadata
instead of Component
and View
For example:
import {ComponentMetadata, ViewMetadata, DynamicComponentLoader, ElementRef } from 'angular2/angular2';
export class App {
static get annotations() {
return [
new ComponentMetadata({
selector: 'app'
}),
new ViewMetadata({
template: '<div #container></b>'
})
];
}
static get parameters() {
return [[DynamicComponentLoader],[ElementRef]];
}
constructor(dynamicComponentLoader, elementRef) {
dynamicComponentLoader.loadIntoLocation(DynamicComponent, elementRef, 'container');
}
}
See this plunker
typescript/eS5-ES6 module is not getting called
To be able to create and use FirstComponent you must decorate it.
It means that you add @Component above the FirstComponent class, it will tell Angular to add meta-data to it.
first.ts:
@Component({
selector: <f-comp></f-comp>, // Here specify a CSS selector used to create the FirstComponent component.
template: `
<p>This is the first component</p>
`
})
export class FirstComponent {
...
}
To be created you must call the FirstComponent inside the app.ts file.
app.ts:
@Component({
directive: [ FirstComponent ], // Here you tell Angular that you are going to use FirstComponent.
...
template: `
...
// here you must put the FirstComponent CSS selector --> <f-comp></f-comp>
`
})
I create a plnkr where I explain every steps and how they works.
http://plnkr.co/edit/CKZ2l9WqMljXM1FO0IoF?p=preview
How to inject upgraded Angular 1 service/factory to Angular 2 component in ES5?
To complete the pixelbits' answer, you need also define my service within the providers list either:
At the application level
document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(Cmp, [MyService]);
});At the component level
var Cmp = ng.core.
Component({
selector: 'cmp',
providers: [ MyService ]
}).
(...)
Class({
constructor: [MyService, function(service) {
}]
});
It depends on what you want to do: share your service instance for all elements in the Angular2 application or per component.
This answers could give more details on dependency injection in Angular2 with ES5: Dependency Injection in Angular 2 with ES5.
Edit
In fact, there is a subtlety. IN the case of upgrade you need not to bootstrap using the ng.platform.browser.bootstrap
function but the one from the upgrade
object.
upgrade.bootstrap(document.body, ['heroApp']);
Where heroApp
is the Angular1 module containing services and factories I want to use in the Angular2 application.
In fact, when you call the upgradeNg1Provider
method on the upgrade, corresponding providers are registered within its associated injector. This means that you don't need to specify them at described above.
So you simply need to do that where you want to inject:
(...)
Class({
constructor: [ ng.core.Inject('customService'),
ng.core.Inject('customFactory'),
function(cService, cFactory) {
}]
});
Miško Hevery provides a great plunkr for this: http://plnkr.co/edit/yMjghOFhFWuY8G1fVIEg?p=preview.
Hope it helps you,
Thierry
How to bootstrap an angular2 component from ES5?
Angular modules were introduced in Angular 2.0.0 RC5 as a direct successor of AngularJS modules.
As it's shown in this ES5 example, it is:
platformBrowserDynamic().bootstrapModule(AppModule);
And the component that is supposed to be bootstrapped should have a respective module defined:
var AppModule = NgModule({
declarations: [AppComponent],
bootstrap: [AppComponent]
})
.Class({ constructor: function () {} });
Related Topics
Set Default Value of JavaScript Object Attributes
How Is a JavaScript String Not an Object
Submitting Rails Form on a Radio Button Selection
How to Convert a String to Bytearray
How to Get Objects Value If Its Name Contains Dots
How to Serialize Dom Node to JSON Even If There Are Circular References
Access Elements of Parent Window from Iframe
Best Way to Display Flash Notices in Rails
How to Call an Async Function Inside a Useeffect() in React
JavaScript Keycode VS Charcode
How Can D3.Transform Be Used in D3 V4
Performance of Key Lookup in JavaScript Object
Swift - Converting JSON Date to Swift Compatible Date