Dependency Injection in Angular
Dependency Injection (DI) is a design pattern used to implement IoC (Inversion of Control), allowing the creation of dependent objects outside of a class and providing those objects to a class in various ways. This tutorial provides an overview of dependency injection in Angular, its key features, and how to use it effectively.
What is Dependency Injection?
Dependency Injection is a pattern that allows a class to receive its dependencies from an external source rather than creating them itself. In Angular, DI is used to provide components and services with the necessary dependencies, promoting modularity, testability, and maintainability.
Key Features of Dependency Injection in Angular
Angular's dependency injection system provides several key features:
- Providers: Define how to create an instance of a dependency.
- Injectors: Maintain a registry of providers and create instances of dependencies as needed.
- Tokens: Identify the type of dependency to be injected.
- Hierarchical Injectors: Allow different injectors to manage their own sets of providers, enabling different parts of the application to use different dependencies.
Creating a Service with Dependency Injection
To demonstrate DI in Angular, let's create a simple service and inject it into a component.
Step 1: Create a Service
First, generate a new service using Angular CLI:
ng generate service my-service
This command creates a new service file. Here's an example of a simple service:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class MyService {
getValue(): string {
return 'Hello from MyService!';
}
}
Step 2: Inject the Service into a Component
Next, inject the service into a component:
import { Component, OnInit } from '@angular/core';
import { MyService } from './my-service.service';
@Component({
selector: 'app-my-component',
template: '<p>{{ value }}</p>'
})
export class MyComponent implements OnInit {
value: string;
constructor(private myService: MyService) {}
ngOnInit(): void {
this.value = this.myService.getValue();
}
}
The MyService
is injected into the component through the constructor. The service's getValue
method is called in the ngOnInit
lifecycle hook, and the returned value is assigned to the value
property.
Providing Services
Services can be provided at different levels:
- Root Level: Services provided at the root level are available throughout the application. This is the default when you use the
@Injectable
decorator withprovidedIn: 'root'
. - Module Level: Services can be provided in a specific module by adding them to the
providers
array of the module's metadata. - Component Level: Services can be provided in a specific component by adding them to the
providers
array of the component's metadata. This creates a new instance of the service for that component and its children.
Injection Tokens
Injection tokens are used to uniquely identify dependencies. Angular provides several built-in tokens, but you can also create custom tokens. Here's an example of creating and using a custom injection token:
import { InjectionToken } from '@angular/core';
export const MY_TOKEN = new InjectionToken<string>('MyToken');
// Providing the token
@NgModule({
providers: [
{ provide: MY_TOKEN, useValue: 'Hello from custom token!' }
]
})
export class AppModule {}
// Injecting the token
@Component({
selector: 'app-token-component',
template: '<p>{{ tokenValue }}</p>'
})
export class TokenComponent {
constructor(@Inject(MY_TOKEN) public tokenValue: string) {}
}
Conclusion
Dependency Injection is a powerful pattern in Angular that promotes modularity, testability, and maintainability. By understanding and using providers, injectors, and tokens effectively, you can build robust and scalable Angular applications. Happy coding!