2

I've been going through the "Heroes" tutorial on official Angular 2 page and when I came to routing, a couple of things didn't make sense. It's about providers.

The part in question is represented as follows. My main component looks like this:

/* app.components */
import {Component} from 'angular2/core';
import {HeroesComponent} from './heroes.component';
import {HeroService} from './hero.service';

@Component({
    selector: 'my-app',
    template: `
        <h1>{{title}}</h1>
        <my-heroes></my-heroes>
    `
    directives: [HeroesComponent],
    providers: [HeroService]

})
export class AppComponent {
    title = 'Tour of Heroes';
    constructor(private _heroService: HeroService) {}
}

and the heroes components looks like this:

/* heroes.component */
import {Component} from 'angular2/core';
import {Hero} from './hero';
import {HeroDetailComponent} from './hero-detail.component';
import {HeroService} from './hero.service';
import {OnInit} from 'angular2/core';

@Component({
    selector: 'my-heroes',
    directives: [HeroDetailComponent],
    template: `
        <h2>My Heroes</h2>
        <ul class="heroes">
            <li *ngFor="#hero of heroes" [class.selected] = "hero === selectedHero" (click)="onSelect(hero)">
                <span class="badge"> {{hero.id}} </span> {{hero.name}}
            </li>
        </ul>
        <my-hero-detail [hero]="selectedHero"></my-hero-detail>
        `
})
export class HeroesComponent implements OnInit {
    heroes: Hero[];
    selectedHero: Hero;
    ngOnInit() {
        this.getHeroes();
    }
    constructor(private _heroService: HeroService) {    }

    getHeroes() {
        this._heroService.getHeroes().then(heroes => this.heroes = heroes);
    }

    onSelect(hero: Hero) {
        this.selectedHero = hero;
    }

}

OK, so my question: in order for this to work, I need to import import {HeroService} from './hero.service'; in both files. However, providers: [HeroService] is only a part of the @Component of app.components. I needn't write this piece of code in heroes.component. How does the heroes.component know which provider to pick? Is it inherited from app.components? And if so, why did I have to write this in both files: import {HeroService} from './hero.service';? Why not just in app.components? Both classes also have the same constructor. I don't know what's going on here, so thanks in advance for any explanation.

uglycode
  • 3,022
  • 6
  • 29
  • 55
  • Yes, it is inherited and injectors are hierarchical. Check this blog [post](https://yakovfain.com/2015/11/23/getting-familiar-with-angular-2-dependency-injection/) and official [documentation](https://angular.io/docs/ts/latest/guide/dependency-injection.html). – kemsky Apr 18 '16 at 18:49

3 Answers3

0

Injectors are hierarchical. In bootstrap() the root provider is initialized, then for each component another child-injector is created which leads to a structure that mimics your components in the DOM.

When Angular instantiates a class (service, component, pipe, ...) it requests an instance of DI and DI tries to resolve the constructor parameters as well. This is recursively done until no further dependencies are necessary to resolve, then the instance is returned.

An instance is requested from the closest injector. If the injector doesn't have a provider for a type (or other key like string or OpaqueToken) the request is forwarded to the parent injector until either a provider is found or the root injector is reached.

For more details see https://angular.io/docs/ts/latest/api/core/Directive-decorator.html

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
0

This is linked to the "hierarchical injectors" feature of Angular2. These injectors are linked to components and follow the same tree.

The injector of a child component is a sub injector of the parent component. If the provider isn't found into the current injector, it will be looked for into the parent one...

See this question for more details:

Community
  • 1
  • 1
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
0

After studied some documentation of Angular-2 I shared my knowledge about dependency-injection and providers that may can help someone.

The Dependency injection is hierarchical and Dependency injection is an important application design pattern. Angular has its own dependency injection framework, and we really can't build an Angular application without it.

Dependencies are singletons within the scope of an injector. In our example, a single HeroService instance is shared among the AppComponent and its HeroComponentchildren.

In your example the AppComponent is the root component of the Heroes feature area. It governs all the child components of this area. Yur HeroService may exposes a getHeroes method that returns the heros data, but none of its consumers need to know that.

A service is nothing more than a class in Angular 2. It remains nothing more than a class until we register it with an Angular injector.

That's why you need to configuring the injector. We don't have to create an Angular injector. Angular creates an application-wide injector for us during the bootstrap process.We do have to configure the injector by registering the providers that create the services our application requires.We can register a provider during bootstrapping. like:

bootstrap(AppComponent,
         [HeroService]); // DISCOURAGED (but works)

The injector now knows about our HeroService. An instance of our HeroService will be available for injection across our entire application.

The preferred approach is to register application providers in application components. Because the HeroService is used within the Heroes feature area — and nowhere else — the ideal place to register it is in the top-level HeroesComponent.

Registering providers in a component

providers:[HeroService], // in your app.component that registers the HeroService

Look closely at the providers part of the @Component metadata.

An instance of the HeroService is now available for injection in this AppComponent and all of its child components.

The AppComponent itself doesn't happen to need the HeroService. But its child HeroComponent does.

Focus on the constructor

Adding a parameter to the constructor.

constructor(private _heroService: HeroService) {
  this._heroService.getHeroes().then(heroes => this.heroes = heroes); // or you can use in OnInit
}

N.B: May no need to use constructor in your appComponent

We're writing in TypeScript and have followed the parameter name with a type annotation, :HeroService. The class is also decorated with the @Component decorator.

When the TypeScript compiler evaluates this class, it sees the @Component decorator and adds class metadata into the generated JavaScript code. Within that metadata lurks the information that associates the heroService parameter with the HeroService class.

That's how the Angular injector knows to inject an instance of the HeroService when it creates a new HeroComponent.

More details for dependency injection can see this link

and for hierarchical-dependency-injection can see this link

Shaishab Roy
  • 16,335
  • 7
  • 50
  • 68