Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Learning Angular 2: Tour of Heroes Tutorial, Dependency Injection

DZone's Guide to

Learning Angular 2: Tour of Heroes Tutorial, Dependency Injection

The fifth installment of the Angular 2 tutorial focuses on dependency injection.

· Web Dev Zone
Free Resource

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

Lesson 5 of the Tour of Heroes tutorial introduces services.

On a recent episode of the Adventures in Angular podcast, guest Pascal Precht made the recommendation that developers should get in the habit of always applying the @Injectable decorator to a service. The reason behind that recommendation is because the decorators (@Component, @Injectable, etc.) cause the emission of metadata needed to work out the dependency injection hierarchy, and there are no negative side-effects to adding the @Injectable decorator to a service even if that service doesn't itself have any dependencies. The tutorial essentially makes the same recommendation.

Image title


The dependency injection (DI) mechanism described in the lesson involves three parts:

  • Using an import statement to import the code of the service module/file.
  • Using a constructor function in the component to assign the module variable defined with the import statement as a property of the component.
  • Adding the module variable defined with the import statement to the list of providers in the component's "providers" metadata property, "providing" a working instance of the service.

At first glance, it appears to be a more verbose process than the Angular 1 approach of listing the dependencies as arguments to the module (though if you wanted to do any minification you needed to document the dependencies in either an inline array or a separate $inject property statement). But you also had to load your modules properly, and of course, all of your JavaScript files containing the dependency modules had to be pulled in via <script> tag upfront. With this syntax, the import statements let you only load the dependent modules you need for this particular component.

I think it's a more explicit syntax that takes some of the mystery out of the DI process. As someone who does a lot of server-side coding, I'm familiar with constructor functions, so for me, it's a matter of thinking of the "providers" property of the component as the arguments that will become part of the call to the constructor function generated by the compiler. You can look in the ES5 output to see the resulting call to the constructor given a provider of "heroService":

var AppComponent = (function () {
    function AppComponent(heroService) {
        this.heroService = heroService;
        this.title = 'Tour of Heroes';
    }

Having hooks into the component lifecycle like ngOnInit provides developers with additional control over their components. With Angular 1 I would end up working with controllers where it was sometimes hard to distinguish the code that would be executed when the controlller loaded from the code that defined event handlers or watched for certain data changes. ngOnInit will provide a container for that initialization code, and run it at a point in the lifecycle where the component is "fully engaged."

Given the discussion in lesson 3 about how ngIf destroys/removes the content it contains rather than just hiding it, I was curious to see if that meant that any sub-component in that ngIf-wrapped content would fire off ngOnInit every time it was restored to the DOM. So I made a really simple HeroSubDetailComponent:

import { Hero } from './hero';
import { Component, Input, OnInit } from '@angular/core';

@Component({
    selector: 'my-hero-subdetail',
    template: `
        <div>
            <span>{{hero.name}} is kinda cool.</span>
        </div>
    `
})

export class HeroSubDetailComponent implements OnInit {
    @Input()
    hero: Hero;

    ngOnInit() {
        console.log( "sub-detail-component initialized" );
    } 
}

... and then added it to the existing HeroDetailComponent within an ngIf block that would only evaluate to true for the first hero:

import { HeroSubDetailComponent } from './hero-sub-detail.component';

@Component({
    selector: 'my-hero-detail',
    directives: [ HeroSubDetailComponent ],
    template: `
    <div>
      <h2>{{hero.name}} details!</h2>
      <div>
        <label>id: </label>
        {{hero.id}}
      </div>
      <div>
        <label>name: </label>
        {{hero.name}}
      </div>
      <div *ngIf="hero.id == 11">
        <my-hero-subdetail [hero]="hero"></my-hero-subdetail>
      </div>
  </div>
    `
})

... and as I suspected, every time the HeroSubDetailComponent was removed then re-added to the DOM as I clicked through different heroes, it would fire ngOnInit on the readd.

I wasn't aware that Promises were added as native constructs in ES2015 (and thus in the latest implementation of TypeScript), so that was something I learned during this lesson. I was momentarily confused by how it was used without instantiating an instance of it: apparently the resolve() method is a static method, so in this case where the data is hard-coded in the application and immediately available it makes sense to go this route. But a note about the fact that in a more traditional use case you would instantiate a Promise instance might be warranted.

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:
angular 2 ,dependency injection ,framework ,javascript ,tutorial

Published at DZone with permission of Brian Swartzfager, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}