So in the first chapter of the series we had a general look at what dependency injection is, how it works and why we use it.
We mentioned that there were 3 elements in the pattern: Client, Service and Injector.
In today's chapter I will talk about the Injectors and their role in dependency injection in angular. So let's get started!
I am sure that if you have used dependency injection in angular you have seen the following patterns:
But when should we use on or the other?
In order to understand when and why to use one or the other we need to explore two important concepts:
- Injector Hierarchy
- Dependency resolution
Angular has two main injector hierarchies that govern which injector is the responsible to provide the dependency to the requesting Client
These hierarchies are the ModuleInjector and the ElementInjector
When we provide a dependency using the
@Directive() providers we are making that dependency available for that component or directive and all its children (example 2 above) and we are inside the ElementInjector hierarchy.
It's interesting to note that for each component instance that holds that provider definition we are creating a new instance of the dependency
Whenever we provide the dependency inside the
@Injectable() decorator. (example 1 above) or inside an NgModule's Providers array we are inside the ModuleInjector hierarchy.
In ModuleInjector the hierarchy is not dependant on any children rather it is a quite well defined hierarchy composed of three injectors:
And finally, when is the NullInjector reached and what does it do? In this case the answer is quite simple, when angular doesn't find the provider for a dependency anywhere it will reach the NullInjector which is the responsible to throw the
NullInjectorError: No provider for dependency
And this takes us to dependency resolution and how does angular traverse the different injectors and determines which Injector should be responsible to provide the dependency
According to angular official documentation angular determines how to resolve a dependency the following way:
- Against its parents in the ElementInjector hierarchy.
- Against its parents in the ModuleInjector hierarchy.
But what does that actually mean?
What angular does when a dependency is injected in a component or directive is try to search firstly inside its ElementInjector, that is in its own providers and its ancestor components providers.
Only then if angular can not find any provider in the ElementInjector, it starts looking in its ModuleInjector going to the root and then the platform injectors and finally reaching the NullInjector which will throw the error we see when we forget to define a provider for our dependency
So this is the default way angular resolves dependencies, there are some other concepts here but we will see them in another chapter of this series
So we have seen that we have two main injector hierarchies in angular:
And we have seen how we can provide in one or the other hierarchy of Injectors
We have also learned that angular will traverse all the ElementInjector hierarchy before moving on to the ModuleInjector hierarchy which will end in the NullInjector if no provider is found.
I hope this helps to clarify when we should provide our dependencies in one way or another and the differences and advantages we can get from it.
If you have any questions regarding this, I will do my best to provide an answer to them.
See you on the next chapter!