DEV Community

Cover image for Angular Dependency Injection: A Complete Guide
Suresh Mohan for Syncfusion, Inc.

Posted on • Originally published at syncfusion.com on

Angular Dependency Injection: A Complete Guide

Dependency injection is one of the most highlighted features in Angular. Angular has its own dependency injection framework, which enforces the constructor injection pattern.

In many cases, developers do not need to pay attention to dependency injection. It just works, and we can use it without thinking about it. However, there are situations where we need to have in-depth knowledge of it.

So, in this article, I will discuss how Angular dependency injection works in detail to give you a better understanding.

What is Angular dependency injection?

This is one of the most frequently asked questions related to Angular. According to the official Angular documentation, “Dependency injection, or DI, is a design pattern in which a class requests dependencies from external sources rather than creating them.”

For better understanding, let’s consider the following example.

Assume that you have a service named articleService , and you need to make several back-end calls from it. Angular HttpClient is widely used in such scenarios, and one of the options would be to create your own dependencies.

export class articleService() { 
  http: HttpClient; 
  constructor() {
    this.http = new HttpClient(dependencies needed by HTTPClient); 
  }
}
Enter fullscreen mode Exit fullscreen mode

Although this seems pretty simple, there are a few issues with this approach. It will create dependencies locally each time, when needed, and testing will become harder since we cannot replace the actual HttpClient with a mock HttpClient.

This is where dependency injection in Angular comes into play. We can create the same articleService without letting the service know how to create its HttpClient dependency.

The improved version of the service with dependency injection would look like this:

@Injectable()
export class articleService() {
  http: HttpClient;
  constructor(http: HttpClient) {     
    this.http = http;   
  }
}
Enter fullscreen mode Exit fullscreen mode

Here, the service class receives all the required dependencies as input parameters, and it is only capable of performing tasks with dependencies. With the @Injectable() decorator, we can move all the dependency creation code away from the service. It allows us to write tests and easily manage the application for multiple environments.

In simple terms, this process of receiving dependencies as input parameters without knowing how they are created is dependency injection.

Use of @Injectable() decorator in Angular

In the above example, we used the @Injectable() decorator to create a service with dependency injection support. It is responsible for linking our service class to the Angular dependency injection system.

For better understanding, let’s consider the same example discussed above, but this time without the @Injectable() decorator.

export class articleService() {
http: HttpClient;
  constructor(http: HttpClient) {     
    this.http = http;   
  }
}
Enter fullscreen mode Exit fullscreen mode

Since we haven’t used the @Injectable() decorator, articleService is not connected to the Angular dependency injection system. If we try to use it in Angular components, it will give us a NullInjectorError error.

@Component({ 
selector: view-articles, 
templateUrl: ./view-articles.component.html, 
styleUrls: [./view-articles.component.css]
})
export class ViewArticlesComponent { 
  constructor(private articleService: ArticleService) { } 
}
Enter fullscreen mode Exit fullscreen mode

This error is because our application is not aware of how to call the articleService constructor to create an instance and pass it as a dependency.

This is a common error developers face when manually creating injectable classes. As a solution, we can easily create service classes with dependency injection support using Angular CLI.

| ng g s articleService |

Advantages of dependency injection

Earlier, I mentioned that dependency injection allows easy test writing by using mock dependencies. Apart from that, there are several other advantages of dependency injection:

  • Makes it simple to manage dependencies.
  • Supports application scaling.
  • Reduces boilerplate code and makes the code more readable and maintainable.
  • Helps to enable loose coupling.
  • Code can be tested with different mock implementations.

I think now you have a basic understanding of Angular dependency injection and how it works. So, let’s move into a little more advanced topic and discuss Angular hierarchical dependency injection.

Angular hierarchical dependency injection

In Angular, there are multiple places to define providers for dependencies. For example, you can define them in module , component, or directive levels. This is known as hierarchical dependency injection in Angular, and it is available from Angular 2 onwards.

So, when you inject a service into a component, Angular will look in the list of providers in that component for the dependency provider. If the dependency provider is not found, the search will move on to the parent components provider list.

Likewise, this process will repeat until it finds the provider or reaches the application’s root component ( AppComponent ). If there are no providers means, Angular will show a “No provider found” error message.

Although this process seems complex, it provides significant benefits for large-scale applications. So, let’s see the advantages of Angular hierarchical dependency injection.

Advantages of Angular hierarchical dependency injection

Dependency sharing is the main advantage of the Angular hierarchical dependency injection mechanism. Overall, we can divide it into two parts:

1. Sharing dependencies between components and modules

Angular is well known for building large-scale applications. It is common to follow a module-based approach in such situations, and there are many components and services within a single module.

These components often share dependencies, and with the hierarchical structure, we can include the dependency provider at the parent level and use all components as necessary.

This behavior also allows developers to follow module architecture for applications.

2. Allows sharing dependencies from isolated sections

Nowadays, it is common to have isolated sections in applications. These sections may have their own private services and dependencies.

However, there can be situations where we need to expose a limited number of dependencies from isolated sections to other parts of the application. With this hierarchical system, we can limit parent components to only share the dependencies with selected children.

Now, some of you might think that we can achieve the same by injecting dependencies to each component individually. You need to understand Angular component hierarchical dependency injection in detail to find the answer for that.

Angular component hierarchical dependency injection in detail

Let’s assume that we have a component named article-details , and that component is iterated in the root application component to display a list of articles.

<div> 
  <article-details *ngFor=”let article of articles [article]=”article”>  
  </article-details> 
</div>
Enter fullscreen mode Exit fullscreen mode

Suppose, we inject articleService to the article-details component and root component individually. In that case, Angular will find the articleService in the list of providers in each component and create a new instance each time. So, iterating the child component five times will create six articleService instances.

However, if we follow hierarchical dependency injection, we only need to include the articleService for the root-level component, and Angular will use the same instance for the child components.

However, Angular dependency injection works a bit differently when it comes to modules.

Angular module hierarchical dependency injection in detail

As mentioned, modules are widely used in Angular applications, and developers tend to use dependency providers at the module level rather than using them at the component level.

Let’s consider the same example and include article-details in a module named ArticleModule. So now, we have two dependency providers in AppComponent and ArticleModule. But, when you run the application, there will be only one instance of Article Service.

The reason behind this behavior is the priority of dependency injection hierarchies. When there are two hierarchies for components and modules, Angular will first go through the component hierarchy. If there are no providers means, it will look in the module hierarchy.

As developers, it is up to you to select the best place to define dependency providers, and you always need to think about your project requirements.

Conclusion

Dependency injection is one of the most important features in Angular. In this article, I introduced dependency injection, how it works, and hierarchical dependency injection concepts.

I hope you found this useful. Thank you for reading.

Syncfusion’s Angular UI components library is the only suite that you will ever need to build an application, containing over 65 high-performance, lightweight, modular, and responsive UI components in a single package.

For existing customers, the newest Essential Studio version is available for download from the License and Downloads page. If you are not yet a Syncfusion customer, you can try our 30-day free trial to check out the available features. Also, check out our demos on GitHub.

You can contact us through our support forums, support portal, or feedback portal. We are always happy to assist you!

Related blogs

Top comments (0)