How does Dependency Injection work in Angular?

Aditya Narayan Gantayat
CodeX
Published in
3 min readOct 10, 2022

We all have used Dependency Injection irrespective of the tech stack, at some point in our coding journey. But what’s the reason for using it?

Imagine instantiating a class each time we need to access it! Awful, yes! We use DI to avoid hard dependencies between classes.

Injecting the MessageService instance in the constructor of the component

Dependency Injection ensures that the component doesn’t create the instance of the service class itself, but instead it delegates the instantiation process to the Injector, which has the responsibility to create the instance of the respective class and inject it into the constructor.

How does the injector resolve dependencies?

The injector basically has a registry where all the instances are created(usually when we use the @Injectable annotation before the class) and stored.

The use of providedIn property of @Injectable decorator to ensure that this service class can be injected as a dependency wherever necessary.

Whenever a component (Component_A in the above example) gets executed, it resolves the dependencies to the existing instances instead of creating a new one. It happens during the compilation time and if the class is not mentioned in the providers array in the @NgModule, or the class is not configured using the providedIn property of @Injectable decorator; it will throw an error saying that it cannot resolve dependencies.

Now that begs the question, what is that ‘root’ for? Right?

For that let’s understand the Injector Hierarchies and Resolving rules:

Injection hierarchies to resolve dependencies

There are two types of Hierarchies as we can see in the picture: Element Injector Hierarchy and the Module Injector Hierarchy.

Component having its own providers array to resolve the dependency

Let’s talk about the Element Injector Hierarchy! It is available to classes declared with @Directive and @Component decorators. Here in this case, the dependency is resolved in the Element Injector tree itself, i.e. within ComponentA itself.

But, what if there was no providers array mentioned inside the component?

As we can see from the injection tree image above, if it cannot resolve the dependency inside the component, then it requests the parent component in the Element Injector tree (Root component in this case) to resolve it. If it has the class provided in the providers array, it resolves the dependency and returns to the ComponentA.

But what if the Root component doesn’t have the providers array either?

It goes back to the ComponentA which requested to resolve the dependency since it was not resolved in the Element Injector tree, which as a result searches for the module in which scope the ComponentA is declared.

Now comes the role of the Module Injector tree.

Since the Child Module is lazy loaded and has the ComponentA declared in its scope, it is delegated with the responsibility to resolve the dependency. If Angular would find the provider for the dependency in the Child Module, it would inject the dependency in ComponentA. But if it doesn’t find any, it would further delegate the resolving to the Root Module injector. Then the same process occurs, if no provider is found, its further delegated to the Platform module injector and then finally the Null Injector which eventually throws an error whenever Angular tries to resolve dependencies there.

Simplified, right?

This article is inspired from Decoded Frontend channel on Youtube.

I’d really like to inject your clap(like) on this article. Don’t delegate it to the next reader, please? xD

--

--