问题
I have been trying to figure out how the (DI) Dependency Injection work in Angular2. I ran into lots of problem/issue every time when I tried to Inject a service/or class into my components.
From different googled articles, I need to either use providers: []
in the Component configuration, or sometimes I need to use @Inject()
in my constructor or inject directly in the bootstrap(app, [service])
? I\'ve also seen some articles want me to put @injectable
decorator.
For example: to inject Http, I only need to import{Http}
and put Http in the providers, but for FormBuilder, I need to use @Inject()
in constructor.
Is there any rule of thumb for when to use what? Could you please provide some example code snippet? Thank you :-)
回答1:
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
回答2:
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 and Accepted Answer
Hope this helps. :)
回答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) {}
回答4:
I'll add a few things that I didn't see mentioned in the other answers. (At the time I'm writing this, that means the answers from Thierry, Günter, and A_Singh).
- Always add
Injectable()
to services you create. Although it is only needed if your service itself needs to inject something, it's a best practice to always include it. - The
providers
array on directives/components and theproviders
array in NgModules are the only two ways to register providers that are not built-in. (Examples of built-in objects that we don't have to register areElementRef
,ApplicationRef
, etc. We can simply inject these.) - When a component has a
providers
array, then that component gets an Angular injector. The injectors are consulted when something wants to inject a dependency (as specified in the constructor). I like to think of the injector tree as a sparer tree than the component tree. The first injector that can satisfy a dependency request does so. This hierarchy of injectors allows dependencies to be singletons or not.
回答5:
Why @Injectable()?
@Injectable() marks a class as available to an injector for instantiation. Generally speaking, an injector will report an error when trying to instantiate a class that is not marked as @Injectable().
As it happens, we could have omitted @Injectable() from our first version of HeroService because it had no injected parameters. But we must have it now that our service has an injected dependency. We need it because Angular requires constructor parameter metadata in order to inject a Logger.
SUGGESTION: ADD @INJECTABLE() TO EVERY SERVICE CLASS We recommend adding @Injectable() to every service class, even those that don't have dependencies and, therefore, do not technically require it. Here's why:
Future proofing: No need to remember @Injectable() when we add a dependency later.
Consistency: All services follow the same rules, and we don't have to wonder why a decorator is missing.
Injectors are also responsible for instantiating components like HeroesComponent. Why haven't we marked HeroesComponent as @Injectable()?
We can add it if we really want to. It isn't necessary because the HeroesComponent is already marked with @Component, and this decorator class (like @Directive and @Pipe, which we'll learn about later) is a subtype of InjectableMetadata. It is in fact InjectableMetadata decorators that identify a class as a target for instantiation by an injector.
Source: https://angular.io/docs/ts/latest/guide/dependency-injection.html
来源:https://stackoverflow.com/questions/36109555/how-to-use-dependency-injection-di-correctly-in-angular2