Use when building modular Angular applications requiring dependency injection with providers, injectors, and services.
Limited to specific tools
Additional assets for this skill
This skill is limited to using the following tools:
name: angular-dependency-injection description: Use when building modular Angular applications requiring dependency injection with providers, injectors, and services. allowed-tools:
Master Angular's dependency injection system for building modular, testable applications with proper service architecture.
Angular's DI is hierarchical and uses decorators and providers:
import { Injectable } from '@angular/core';
// Service injectable at root level
@Injectable({
providedIn: 'root'
})
export class UserService {
private users: User[] = [];
getUsers(): User[] {
return this.users;
}
addUser(user: User): void {
this.users.push(user);
}
}
// Component injection
import { Component } from '@angular/core';
@Component({
selector: 'app-user-list',
template: `
<div *ngFor="let user of users">
{{ user.name }}
</div>
`
})
export class UserListComponent {
users: User[];
constructor(private userService: UserService) {
this.users = this.userService.getUsers();
}
}
import { Injectable, Provider } from '@angular/core';
// Interface
interface Logger {
log(message: string): void;
}
// Implementations
@Injectable()
export class ConsoleLogger implements Logger {
log(message: string): void {
console.log(message);
}
}
@Injectable()
export class FileLogger implements Logger {
log(message: string): void {
// Write to file
}
}
// Provider configuration
const loggerProvider: Provider = {
provide: Logger,
useClass: ConsoleLogger // or FileLogger based on env
};
// In module
@NgModule({
providers: [loggerProvider]
})
export class AppModule {}
// Usage
export class MyComponent {
constructor(private logger: Logger) {
this.logger.log('Component initialized');
}
}
import { InjectionToken } from '@angular/core';
// Configuration object
export interface AppConfig {
apiUrl: string;
timeout: number;
retries: number;
}
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');
// Provider
const configProvider: Provider = {
provide: APP_CONFIG,
useValue: {
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3
}
};
// Module
@NgModule({
providers: [configProvider]
})
export class AppModule {}
// Usage
export class ApiService {
constructor(@Inject(APP_CONFIG) private config: AppConfig) {
console.log(this.config.apiUrl);
}
}
import { Injectable, InjectionToken } from '@angular/core';
export const API_URL = new InjectionToken<string>('api.url');
// Factory function
export function apiUrlFactory(config: AppConfig): string {
return config.production
? 'https://api.prod.example.com'
: 'https://api.dev.example.com';
}
// Provider
const apiUrlProvider: Provider = {
provide: API_URL,
useFactory: apiUrlFactory,
deps: [AppConfig] // Dependencies for factory
};
// Complex factory with multiple deps
export function httpClientFactory(
handler: HttpHandler,
config: AppConfig,
logger: Logger
): HttpClient {
logger.log('Creating HTTP client');
return new HttpClient(handler);
}
const httpClientProvider: Provider = {
provide: HttpClient,
useFactory: httpClientFactory,
deps: [HttpHandler, AppConfig, Logger]
};
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class NewLogger {
log(message: string): void {
console.log('[NEW]', message);
}
}
// Alias old logger to new logger
const oldLoggerProvider: Provider = {
provide: 'OldLogger',
useExisting: NewLogger
};
// Usage
export class MyComponent {
constructor(
@Inject('OldLogger') private logger: NewLogger
) {
this.logger.log('Using aliased logger');
}
}
import { InjectionToken } from '@angular/core';
// Primitive token
export const MAX_RETRIES = new InjectionToken<number>('max.retries', {
providedIn: 'root',
factory: () => 3 // Default value
});
// Object token
export interface FeatureFlags {
enableNewUI: boolean;
enableBeta: boolean;
}
export const FEATURE_FLAGS = new InjectionToken<FeatureFlags>(
'feature.flags',
{
providedIn: 'root',
factory: () => ({
enableNewUI: false,
enableBeta: false
})
}
);
// Usage
@Injectable()
export class ApiService {
constructor(
@Inject(MAX_RETRIES) private maxRetries: number,
@Inject(FEATURE_FLAGS) private flags: FeatureFlags
) {}
}
// Not type-safe, avoid when possible
const providers: Provider[] = [
{ provide: 'API_URL', useValue: 'https://api.example.com' },
{ provide: 'TIMEOUT', useValue: 5000 }
];
// Usage
export class MyService {
constructor(
@Inject('API_URL') private apiUrl: string,
@Inject('TIMEOUT') private timeout: number
) {}
}
// Singleton across entire app
@Injectable({
providedIn: 'root'
})
export class GlobalService {
private state = {};
}
// Same instance everywhere
@Injectable()
export class ModuleService {
// Service specific to module
}
@NgModule({
providers: [ModuleService]
})
export class FeatureModule {}
// Different instance per module
@Injectable()
export class ComponentService {
private data = [];
}
@Component({
selector: 'app-my-component',
template: '...',
providers: [ComponentService] // New instance per component
})
export class MyComponent {
constructor(private service: ComponentService) {}
}
// Each component instance gets its own service instance
@Directive({
selector: '[appHighlight]',
providers: [DirectiveService]
})
export class HighlightDirective {
constructor(private service: DirectiveService) {}
}
// Each directive instance gets its own service
// Root - singleton
@Injectable({
providedIn: 'root'
})
export class RootService {}
// Platform - shared across multiple apps
@Injectable({
providedIn: 'platform'
})
export class PlatformService {}
// Any - new instance per module
@Injectable({
providedIn: 'any'
})
export class AnyService {}
// Module - specific module
@Injectable({
providedIn: FeatureModule
})
export class FeatureService {}
import { InjectionToken } from '@angular/core';
// Token for multiple providers
export const HTTP_INTERCEPTORS =
new InjectionToken<HttpInterceptor[]>('http.interceptors');
// Multiple implementations
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler) {
// Add auth header
return next.handle(req);
}
}
@Injectable()
export class LoggingInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler) {
// Log request
return next.handle(req);
}
}
// Register as multi-providers
const providers: Provider[] = [
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true
},
{
provide: HTTP_INTERCEPTORS,
useClass: LoggingInterceptor,
multi: true
}
];
// Inject as array
export class HttpService {
constructor(
@Inject(HTTP_INTERCEPTORS) private interceptors: HttpInterceptor[]
) {
// interceptors is array of all registered interceptors
}
}
import { Optional } from '@angular/core';
@Injectable()
export class MyService {
constructor(
@Optional() private logger?: Logger
) {
// logger might be undefined
this.logger?.log('Service created');
}
}
import { Self } from '@angular/core';
@Component({
selector: 'app-my-component',
providers: [LocalService]
})
export class MyComponent {
constructor(
@Self() private local: LocalService // Only from this component
) {}
}
import { SkipSelf } from '@angular/core';
@Component({
selector: 'app-child',
providers: [SharedService]
})
export class ChildComponent {
constructor(
@SkipSelf() private parent: SharedService // From parent, not self
) {}
}
import { Host } from '@angular/core';
@Directive({
selector: '[appChild]'
})
export class ChildDirective {
constructor(
@Host() private parent: ParentComponent // From host component
) {}
}
import { NgModule, ModuleWithProviders } from '@angular/core';
@NgModule({})
export class SharedModule {
// For root module - configures services
static forRoot(config: SharedConfig): ModuleWithProviders<SharedModule> {
return {
ngModule: SharedModule,
providers: [
SharedService,
{
provide: SHARED_CONFIG,
useValue: config
}
]
};
}
// For feature modules - no service providers
static forChild(): ModuleWithProviders<SharedModule> {
return {
ngModule: SharedModule,
providers: [] // No providers, use root services
};
}
}
// Usage in AppModule
@NgModule({
imports: [
SharedModule.forRoot({ apiUrl: 'https://api.example.com' })
]
})
export class AppModule {}
// Usage in feature module
@NgModule({
imports: [
SharedModule.forChild()
]
})
export class FeatureModule {}
// Traditional (not tree-shakable)
@Injectable()
export class OldService {}
@NgModule({
providers: [OldService]
})
export class AppModule {}
// Tree-shakable (preferred)
@Injectable({
providedIn: 'root'
})
export class NewService {}
// No need to register in module
// Service is removed if never injected
import { TestBed } from '@angular/core/testing';
describe('MyComponent', () => {
let mockUserService: jasmine.SpyObj<UserService>;
beforeEach(() => {
mockUserService = jasmine.createSpyObj('UserService', ['getUsers']);
TestBed.configureTestingModule({
declarations: [MyComponent],
providers: [
{ provide: UserService, useValue: mockUserService }
]
});
});
it('should get users', () => {
mockUserService.getUsers.and.returnValue([]);
const fixture = TestBed.createComponent(MyComponent);
// Test component with mock
});
});
import { TestBed } from '@angular/core/testing';
describe('UserService', () => {
let service: UserService;
let httpMock: jasmine.SpyObj<HttpClient>;
beforeEach(() => {
httpMock = jasmine.createSpyObj('HttpClient', ['get', 'post']);
TestBed.configureTestingModule({
providers: [
UserService,
{ provide: HttpClient, useValue: httpMock }
]
});
service = TestBed.inject(UserService);
});
it('should fetch users', () => {
httpMock.get.and.returnValue(of([]));
service.getUsers().subscribe();
expect(httpMock.get).toHaveBeenCalled();
});
});
Use angular-dependency-injection when building modern, production-ready applications that require:
providedIn: 'root' - Tree-shakable and singletonimport { Injectable, Inject, InjectionToken } from '@angular/core';
export interface ApiConfig {
baseUrl: string;
timeout: number;
}
export const API_CONFIG = new InjectionToken<ApiConfig>('api.config');
@Injectable({
providedIn: 'root'
})
export class ApiService {
constructor(@Inject(API_CONFIG) private config: ApiConfig) {}
get(endpoint: string) {
return fetch(`${this.config.baseUrl}/${endpoint}`, {
signal: AbortSignal.timeout(this.config.timeout)
});
}
}
// Module
@NgModule({
providers: [
{
provide: API_CONFIG,
useValue: {
baseUrl: 'https://api.example.com',
timeout: 5000
}
}
]
})
export class AppModule {}
import { Injectable } from '@angular/core';
// Abstract class
export abstract class DataService<T> {
abstract get(id: string): Observable<T>;
abstract save(item: T): Observable<T>;
}
// Implementation
@Injectable()
export class UserDataService implements DataService<User> {
get(id: string): Observable<User> {
// Implementation
}
save(user: User): Observable<User> {
// Implementation
}
}
// Provider
@NgModule({
providers: [
{ provide: DataService, useClass: UserDataService }
]
})
export class FeatureModule {}
// Usage
export class MyComponent {
constructor(private dataService: DataService<User>) {}
}
import { Injectable, InjectionToken } from '@angular/core';
import { environment } from './environments/environment';
@Injectable()
export class DevLogger {
log(message: string) {
console.log('[DEV]', message);
}
}
@Injectable()
export class ProdLogger {
log(message: string) {
// Send to logging service
}
}
// Factory chooses implementation
export function loggerFactory(): Logger {
return environment.production
? new ProdLogger()
: new DevLogger();
}
const loggerProvider: Provider = {
provide: Logger,
useFactory: loggerFactory
};
// Parent service
@Injectable({
providedIn: 'root'
})
export class GlobalState {
count = 0;
}
// Child service (isolated)
@Injectable()
export class LocalState {
count = 0; // Independent per component
}
@Component({
selector: 'app-counter',
providers: [LocalState] // New instance per component
})
export class CounterComponent {
constructor(
public global: GlobalState,
public local: LocalState
) {}
incrementGlobal() {
this.global.count++; // Affects all components
}
incrementLocal() {
this.local.count++; // Only this component
}
}