How to use Dependency Injection (DI) correctly in Angular2?
Solution 1:
Broad question, TL;DR version
@Injectable()
is a decorator which tells the
typescript
that decorated class hasdependencies
and does not mean that this class can be injected in some other.And then TypeScript understands that it needs to Inject the required metadata into decorated class when constructing, by using the
imported
dependencies.
bootstrap(app, [service])
bootstrap() takes care of creating a root injector for our application when it’s bootstrapped. It takes a list of providers as second argument which will be passed straight to the injector when it is created.
You bootstrap your application with the services that are gonna be used in many places like
Http
, which also means you'll not need to writeproviders: [Http]
in your class configuration.
providers: [service]
providers also does the work of passing all the services' arguments to
Injector
.You put services in providers if it's not
bootstrap()
ped with. And is needed only in a few places.
@Inject()
- is
also a decoratora function that does the work of actually injecting those services
like this.constructor(@Inject(NameService) nameService)
- but if you use TS all you need to do is this
constructor(nameService: NameService)
and typescript will handle the rest.
Further Reading
If you want to dig deep in DI, take a look at this amazing article
and to understand Decorators vs Annotations see this.
Here is the official guide.
Gunter's Answer
Mark Rajcok's Answer
andAccepted Answer
Hope this helps. :)
Solution 2:
Dependency injection in Angular2 relies on hierarchical injectors that are linked to the tree of components.
This means that you can configure providers at different levels:
- For the whole application when bootstrapping it. In this cases, all sub injectors (the component ones) will see this provider and share the instance associated with. When interacting, it will be the same instance
- For a specific component and its sub components. Same as before but for à specific component. Other components won't see this provider. If you redefine something defined above (when bootstrapping for example), this provider will be used instead. So you can override things.
- For services. There are no providers associated with them. They use ones of the injector from the element that triggers (directly = a component or indirectly = a component that triggers the call of service chain)
Regarding your other questions:
- @Injectable. To inject into a class, you need a decorator. Components have one (the @Component one) but services are simple classes. If a service requires dependencies to be injected in it, you need this decorator.
- @Inject. In most times, the type of constructor parameters is enough to let Angular2 determines what to inject. In some cases (for example, if you explicitly use an OpaqueToken and not a class to register providers), you need to specify some hints about what to inject. In such cases, you need to use @Inject.
See these questions for additional details:
- What's the best way to inject one service into another in angular 2 (Beta)?
- Angular2: Inject a non @Injectable class
- Inject all Services that implement some Interface
Solution 3:
I need to either use providers: []
For dependency injection to be able to create instances for you, you need to register providers for these classes (or other values) somewhere.
Where you register a provider determines the scope of the created value.
Angulars DI is hierarchical.
If you register a provider at the root of the tree
>=RC.5
@NgModule({
providers: [/*providers*/]
...
})
or for lazy loaded modules
static forRoot(config: UserServiceConfig): ModuleWithProviders {
return {
ngModule: CoreModule,
providers: [
{provide: UserServiceConfig, useValue: config }
]
};
}
<=RC.4
(bootstrap(AppComponent, [Providers})
or @Component(selector: 'app-component', providers: [Providers])
(root component)
then all components and services that request an instance get the same instance.
If a provider is registered in one of the child components a new (different) instance is provided for descendants of this component.
If a component requests an instance (by a constructor parameter), DI looks "upwards" the component tree (starting from leaf towards the root) and takes the first provider it finds. If an instance for this provider was already created previously, this instance is used, otherwise a new instance is created.
@Inject()
When a component or service requests a value from DI like
constructor(someField:SomeType) {}
DI looks up the provider by the type SomeType
. If @Inject(SomeType)
is added
constructor(@Inject(SomeType) someField:SomeType) {}
DI looks up the provider by the parameter passed to @Inject()
. In the above example the parameter passed to @Inject()
is the same as the type of the parameter, therefore @Inject(SomeType)
is redundant.
However there are situations where you want to customize the behavior for example to inject a configuration setting.
constructor(@Inject('someName') someField:string) {}
The type string
isn't sufficient to distinguish a specific configuration setting when you have a several registered.
The configuration value needs to be registered as provider somewhere like
>=RC.5
@NgModule({
providers: [{provide: 'someName', useValue: 'abcdefg'})]
...
})
export class AppModule {}
<=RC.4
bootstrap(AppComponent, [provide('someName', {useValue: 'abcdefg'})])
Therefor you don't need @Inject()
for FormBuilder
if the constructor looks like
constructor(formBuilder: FormBuilder) {}