Overview
Angular, a powerful and feature-rich front-end framework, is known for its robust dependency injection system. This system is designed to provide and share instances of services and other objects throughout your application. In this article, we will explore hierarchical injection in Angular, understand its significance, and delve into how it works.
What is Hierarchical Injection?
Hierarchical injection is a core concept in Angular's dependency injection system. It's built on the idea that Angular applications have a hierarchical structure. In this hierarchy, components are nested within other components, forming a parent-child relationship. Hierarchical injection allows services to be provided at different levels of this hierarchy.
When we provide a dependency on a component, the same instance of that dependency is injected in component class and all its child components and their child components. This is called a hierarchical injection.
In a hierarchical injection system:
- Each component, along with its children, has its own injector.
- Services can be provided at the root level, which makes them accessible throughout the entire application, or at a component level, making them accessible only at that component and its descendants.
The hierarchical nature of Angular's dependency injection system ensures that components can access the services they need without interfering with or being affected by services used in other parts of the application.
Understanding Hierarchical Injection in Angular
Let's explore how hierarchical injection works in Angular:
1.Root Injector
At the top of hierarchy, we have the root injector. This injector provides services that are accessible to the entire application. These services are often defined in the AppModule
or in service itself. Here's how you typically provide a service at the root level:
Injecting through Service:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class MyService {
// Service logic here
}
Injecting through App Module:
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { DataService } from './data.service';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [DataService], // Providing DataService at the root level
bootstrap: [AppComponent]
})
export class AppModule { }
In this AppModule, we import the DataService and include it in the providers array of the NgModule metadata. By including it here, Angular knows to provide the DataService at the root level.
Note:
- When we provide a dependency on root component, same instance of that dependency is injected to all components, directives and services.
2. Component Injector
Each component in Angular has its own injector. Services provided in a component's providers
array are accessible to that component and its descendants. Here's how you can provide a service at the component level:
@Component({
selector: 'app-my-component',
template: '<p>My Component</p>',
providers: [MyService] // This service is accessible to this component and its descendants
})
export class MyComponent {
// Component logic here
}
Note: When we provide a service on multiple components, each components gets its own instance of that service.
If we have a parent component (Parent
) and we injected service to it using providers
array. Then the same instance of service is available for all its child components, their child components and so on. But if we provided the same in providers array in on of its child, then that child will have a different instance of that service this is known as dependency override.
Dependency Override: When we provide a dependency on a component and we also provide a dependency on its child component, child component dependency instance will override its parent component dependency instance.
3. Accessing Services
In a hierarchical injection system, components can access services provided at their level or higher. For example, if MyService
is provided at the root level and MyComponent
wants to use it, it can do so without any additional configuration:
@Component({
selector: 'app-my-component',
template: '<p>My Component</p>',
})
export class MyComponent {
constructor(private myService: MyService) {
// Access the service
}
}
Angular's injector hierarchy allows you to keep your services well-organized and scoped to the parts of your application where they are needed.
When to use Hierarchical Injection
Hierarchical injection is particularly useful in scenarios where you want to:
- Share data or functionality within a specific part of your application.
- Avoid interfering with other parts of your application that have their own services.
A common use case for hierarchical injection is when you want to create a service specific to a particular feature module or component. By providing the service at the component level, you ensure that it's accessible only within that feature's scope.