17

Let's say we have a Component called Comp and two @Injectable providers called P1 and P2.

P1 needs an instance of P2. P1 is injected into Comp.

It's working perfectly if I declare both providers on Comp like this:

@Component ({
    providers: [P1, P2]
})
export class Comp { ... }

Now, what I would like to do is to declare that P1 needs P2 directly inside P1:

@Component ({
    providers: [P1]
})
export class Comp { ... }


@Injectable(/** Inject P2 here **/)
export class P1 { ... }

How to achieve this?

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
Marcos J.C Kichel
  • 6,887
  • 8
  • 38
  • 78

5 Answers5

14

In fact, injectors can be only configured at the component level or when bootstrapping the application.

When you set providers at the component level, every classes involved in the processing will have a access to these providers: sub components, services. But you can't configure providers for services themselves.

If you configure providers at the bootstrap level (when calling the bootstrap function), all elements in the application will be able to use these providers.

In fact, dependency injector of Angular2 leverages hierarchical injectors. This means that if the provider isn't found at a level, it will be look for in the level above and so on.

Here is an overview of all these elements and there relations:

Application
(providers defined in bootstrap)
     |
AppComponent
(providers defined in the providers attribute)
     |
ChildComponent
(providers defined in the providers attribute)
  getData()     --- Service1 --- Service2

To be able to use Service2 in Service1, the corresponding provider must be found in the providers tree.

In such application, we have three injectors:

  • The application injector that can be configured using the second parameter of the bootstrap function
  • The AppComponent injector that can be configured using the providers attribute of this component. It can "see" elements defined in the application injector. This means if a provider isn't found in this provider, it will be automatically look for into this parent injector. If not found in the latter, a "provider not found" error will be thrown.
  • The ChildComponent injector that will follow the same rules than the AppComponent one. To inject elements involved in the injection chain executed forr the component, providers will be looked for first in this injector, then in the AppComponent one and finally in the application one.

This answer could give you more details about the hierarchical injectors:

heringer
  • 2,698
  • 1
  • 20
  • 33
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
3

You have two options:

  1. As mentioned in the other answer, you have to put your services in your component's providers array, like you did:

    providers: [P1, P2]
    

    And in P1's constructor just inject it:

    export class P1 {
        constructor(private p2: P2){}
    }
    
  2. If you don't want to put it in the providers array, you can do it in the bootstrap method that bootstraps your main component:

    bootstrap(AppComponent, [P2]);
    

And again, just inject it the same way in P1

I don't recommend the second one, and the creators of Angular 2 aren't recommending it either. If you want a global service available everywhere (in other services and components, just put it in the providers array of the root component)

Source: https://angular.io/docs/ts/latest/guide/dependency-injection.html

We do have to configure the injector by registering the providers that create the services we need in our application. We'll explain what providers are later in this chapter. Before we do, let's see an example of provider registration during bootstrapping:

// Injecting services in bootstrap works but is discouraged

bootstrap(AppComponent, [HeroService]);

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

Of course we can't help wondering about that comment telling us not to do it this way. It will work. It's just not a best practice. The bootstrap provider option is intended for configuring and overriding Angular's own pre-registered services.

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

Szabolcs Dézsi
  • 8,743
  • 21
  • 29
1

Injectables support dependency Injection in the same way that components do, through constructor injection.

@Injectable()
export class P1 {
    constructor(private p2: P2){}
}

You can declare it as a provider available to the entire application by adding it to the bootstrap

bootstrap(MyAppComponent, [P1, P2, ...]);
SnareChops
  • 13,175
  • 9
  • 69
  • 91
1

There is an open issue for that

https://github.com/angular/angular/issues/5622

It doesn't look too likely this will be implemented :-/

Currently providers can only be declared at components/directives/pipes and in bootstrap(). Also a limitation with providers at components/directives/pipes is that they can't be overridden in a global configuration.

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

You can do it in this way:

import {Injectable, Inject} from 'angular2/core';
import {Http, Response, Headers, RequestOptions} from 'angular2/http';
import {Observable} from 'rxjs/Rx'; 
import {P2} from '../p2/p2.service'; // path to P2

@Injectable(/** Inject P2 here **/)
export class P1 { 
  private _p2Service: P2

  constructor(private http: Http) {
    this._p2Service = new P2(this.http);
  }

  getProcessedP2Data() {
    ... create your Rx obserwable here, read async data from P2, prcess them, and return obserwable (subscriable) object
  }
}

You will also probably need to create your own RxJS obserwable to return processed data from P2. Look here for details: https://egghead.io/lessons/rxjs-creating-an-observable

Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345