In the last article, we’ve explored basic differences between Angular 1 and 2 with respect to Component over Directive, TypeScript over ES5, ES6 Class over Controller, and Decorators/Annotations. We’ve pretty much got acquainted with setting up Angular2 in TypeScript with SystemJS to begin our journey with Angular2 in coming weeks. In this article, we’ll investigate how dependency injection that we always loved is different in Angular2 than Angular1. We are not going to cover what dependency injection is in this article though, so if you are not familiar with it yet, follow the documentation for more information.
Angular1
As usual, we’ll quickly go through the Angular1 example. Let’s take a simple example (HTML and JS) from my free ebook on Angular1 Directives that explores how DI works in Angular1. Notice we’ve used ngController directive in which we are going to inject a custom service. Also stare at ngApp directive which was a recommended way to bootstrap an application automatically in Angular1 rather than manually using angular.bootstrap
method.
<br> <html ng-app="DI"><br> <head><br> <title>AngularJS: DI</title><br> <script src="../bower_components/angular/angular.js"></script><br> <script src="../js/ch01/di.js"></script><br> </head><br> <body ng-controller="SayHi"></p> <p><h1>Check the console</h1></p> <p></body><br> </html><br>
Here is the JavaScript code for the above example. We have defined the custom service named Hello
and later injected the same into the controller called SayHi
.
<br> var App = angular.module('DI', []);<br> App.service('Hello', function() {<br> var self = this;<br> self.greet = function(who) {<br> self.name = 'Hello ' + who;<br> }<br> });</p> <p>App.controller('SayHi', function(Hello) {<br> Hello.greet('AngularJS');<br> console.log(Hello.name);<br> });<br>
Now that we’ve our demo up and running, it’s time to migrate it to Angular2.
Angular2
Let us first update the JS code first and walk through the changes.
<br> import { NgModule, Component, Injectable } from '@angular/core';<br> import { BrowserModule } from '@angular/platform-browser';<br> import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';</p> <p>@Injectable()<br> class HelloService {<br> name: string;</p> <p> greet(who) {<br> this.name = 'Hello ' + who;<br> }<br> }</p> <p>@Component({<br> selector: 'ng-app',<br> template: '<h1>Check the console</h1>'<br> })<br> export class DIComponent {<br> constructor(public Hello: HelloService) {<br> this.Hello.greet('Angular2');<br> console.log(this.Hello.name);<br> }<br> }</p> <p>@NgModule({<br> declarations: [DIComponent],<br> imports: [BrowserModule],<br> providers: [HelloService],<br> bootstrap: [DIComponent]<br> })<br> export default class MyAppModule {}</p> <p>platformBrowserDynamic().bootstrapModule(MyAppModule);<br>
We are already familiar with Component, ES6 Class, bootstrap, etc. jargons from the previous article as these are the backbone of Angular2. The only difference here is the service, HelloService
. However, if you notice, the service is itself a ES6 Class similar to our application component, DI
. Just like @Component
annotation is there to tell Angular2 to treat a ES6 class as a component, the @Injectable
annotation annotated the ES6 Class as an Angular2 Service that is injected elsewhere.
In the above example, we’ve injected the service via providers
options in the @NgModule
. This way the instance of the service will be available for the entire component only and its child components, if any. However, services in Angular2 are not singleton by nature unlike Angular1 which means if the same service is injected as a provider in a child component, a new instance of the service will be created. This is a confusing thing for beginners so watch out.
The public Hello: HelloService
parameter in the constructor is equivalent to Hello: HelloService = new HelloService();
. It means Hello
is an instance of HelloService
and of a type HelloService
(optional). One benefit of annotations/decorators in Angular2 in my opinion is that there is no confusion over Factory vs Service vs Provider, instead it’s just a ES6 class.
Slight changes compared to the previous example is that we need to give more configurations in SystemJS for typescript compilers to emit decorator MetaData so that generated ES5 code will work fine in a browser.
<br> {<br> "compilerOptions": {<br> "target": "ES5",<br> "module": "commonjs",<br> "moduleResolution": "node",<br> "sourceMap": true,<br> "emitDecoratorMetadata": true,<br> "experimentalDecorators": true,<br> "removeComments": false,<br> "noImplicitAny": false<br> }<br> }<br>
Remember experimentalDecorators
flag enables experimental support for ES7 decorators in TypeScript and emitDecoratorMetadata
flag emits design-type metadata for decorated declaration in ES5 output, especially, for public Hello: HelloService
code in the constructor as we’d seen before. Using the alternative mentioned above will not need these flags though.
Now let us update HTML markup.
<br> <html><br> <head><br> <title>Angular2: DI</title><br> <meta charset="UTF-8"><br> <meta name="viewport" content="width=device-width, initial-scale=1"></p> <p> <!-- 1. Load libraries --><br> <script src="../node_modules/core-js/client/shim.min.js"></script><br> <script src="../node_modules/zone.js/dist/zone.js"></script><br> <script src="../node_modules/reflect-metadata/Reflect.js"></script><br> <script src="../node_modules/systemjs/dist/system.src.js"></script></p> <p> <!-- 2. Configure SystemJS --><br> <script src="../systemjs.config.js"></script><br> <script><br> System.import('ch01/di').catch(function(err){ console.error(err); });<br> </script><br> </head><br> <body><br> <!-- 3. Display the application --><br> <ng-app>Loading...</ng-app><br> </body><br> </html><br>
Wrap up
That’s it guys..! Wolksoftware‘s blog has covered a great intro to reflection in Typescript, if interested. In the next post, we’ll explore two-way databinding in Angular2, but meanwhile Checkout Dependency Injection in action.