Use when adding new error messages to React, or seeing "unknown error code" warnings.
npx skills add analogjs/angular-skills --skill "angular-di"
Install specific skill from multi-skill repository
# Description
Implement dependency injection in Angular v20+ using inject(), injection tokens, and provider configuration. Use for service architecture, providing dependencies at different levels, creating injectable tokens, and managing singleton vs scoped services. Triggers on service creation, configuring providers, using injection tokens, or understanding DI hierarchy.
# SKILL.md
name: angular-di
description: Implement dependency injection in Angular v20+ using inject(), injection tokens, and provider configuration. Use for service architecture, providing dependencies at different levels, creating injectable tokens, and managing singleton vs scoped services. Triggers on service creation, configuring providers, using injection tokens, or understanding DI hierarchy.
Angular Dependency Injection
Configure and use dependency injection in Angular v20+ with inject() and providers.
Basic Injection
Using inject()
Prefer inject() over constructor injection:
import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { UserService } from './user.service';
@Component({
selector: 'app-user-list',
template: `...`,
})
export class UserListComponent {
// Inject dependencies
private http = inject(HttpClient);
private userService = inject(UserService);
// Can use immediately
users = this.userService.getUsers();
}
Injectable Services
import { Injectable, inject, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root', // Singleton at root level
})
export class UserService {
private http = inject(HttpClient);
private users = signal<User[]>([]);
readonly users$ = this.users.asReadonly();
async loadUsers() {
const users = await firstValueFrom(
this.http.get<User[]>('/api/users')
);
this.users.set(users);
}
}
Provider Scopes
Root Level (Singleton)
// Recommended: providedIn
@Injectable({
providedIn: 'root',
})
export class AuthService {}
// Alternative: in app.config.ts
export const appConfig: ApplicationConfig = {
providers: [
AuthService,
],
};
Component Level (Instance per Component)
@Component({
selector: 'app-editor',
providers: [EditorStateService], // New instance for each component
template: `...`,
})
export class EditorComponent {
private editorState = inject(EditorStateService);
}
Route Level
export const routes: Routes = [
{
path: 'admin',
providers: [AdminService], // Shared within this route tree
children: [
{ path: '', component: AdminDashboardComponent },
{ path: 'users', component: AdminUsersComponent },
],
},
];
Injection Tokens
Creating Tokens
import { InjectionToken } from '@angular/core';
// Simple value token
export const API_URL = new InjectionToken<string>('API_URL');
// Object token
export interface AppConfig {
apiUrl: string;
features: {
darkMode: boolean;
analytics: boolean;
};
}
export const APP_CONFIG = new InjectionToken<AppConfig>('APP_CONFIG');
// Token with factory (self-providing)
export const WINDOW = new InjectionToken<Window>('Window', {
providedIn: 'root',
factory: () => window,
});
export const LOCAL_STORAGE = new InjectionToken<Storage>('LocalStorage', {
providedIn: 'root',
factory: () => localStorage,
});
Providing Token Values
// app.config.ts
export const appConfig: ApplicationConfig = {
providers: [
{ provide: API_URL, useValue: 'https://api.example.com' },
{
provide: APP_CONFIG,
useValue: {
apiUrl: 'https://api.example.com',
features: { darkMode: true, analytics: true },
},
},
],
};
Injecting Tokens
@Injectable({ providedIn: 'root' })
export class ApiService {
private apiUrl = inject(API_URL);
private config = inject(APP_CONFIG);
private window = inject(WINDOW);
getBaseUrl(): string {
return this.apiUrl;
}
}
Provider Types
useClass
// Provide implementation
{ provide: LoggerService, useClass: ConsoleLoggerService }
// Conditional implementation
{
provide: LoggerService,
useClass: environment.production
? ProductionLoggerService
: ConsoleLoggerService,
}
useValue
// Static values
{ provide: API_URL, useValue: 'https://api.example.com' }
// Configuration objects
{ provide: APP_CONFIG, useValue: { theme: 'dark', language: 'en' } }
useFactory
// Factory with dependencies
{
provide: UserService,
useFactory: (http: HttpClient, config: AppConfig) => {
return new UserService(http, config.apiUrl);
},
deps: [HttpClient, APP_CONFIG],
}
// Async factory (not recommended - use APP_INITIALIZER)
{
provide: CONFIG,
useFactory: () => fetch('/config.json').then(r => r.json()),
}
useExisting
// Alias to existing provider
{ provide: AbstractLogger, useExisting: ConsoleLoggerService }
// Multiple tokens pointing to same instance
providers: [
ConsoleLoggerService,
{ provide: Logger, useExisting: ConsoleLoggerService },
{ provide: ErrorLogger, useExisting: ConsoleLoggerService },
]
Injection Options
Optional Injection
@Component({...})
export class MyComponent {
// Returns null if not provided
private analytics = inject(AnalyticsService, { optional: true });
trackEvent(name: string) {
this.analytics?.track(name);
}
}
Self, SkipSelf, Host
@Component({
providers: [LocalService],
})
export class ParentComponent {
// Only look in this component's injector
private local = inject(LocalService, { self: true });
}
@Component({...})
export class ChildComponent {
// Skip this component, look in parent
private parentService = inject(ParentService, { skipSelf: true });
// Only look up to host component
private hostService = inject(HostService, { host: true });
}
Multi Providers
Collect multiple values for same token:
// Token for multiple validators
export const VALIDATORS = new InjectionToken<Validator[]>('Validators');
// Provide multiple values
providers: [
{ provide: VALIDATORS, useClass: RequiredValidator, multi: true },
{ provide: VALIDATORS, useClass: EmailValidator, multi: true },
{ provide: VALIDATORS, useClass: MinLengthValidator, multi: true },
]
// Inject as array
@Injectable()
export class ValidationService {
private validators = inject(VALIDATORS); // Validator[]
validate(value: string): ValidationError[] {
return this.validators
.map(v => v.validate(value))
.filter(Boolean);
}
}
HTTP Interceptors (Multi Provider)
// Interceptors use multi providers internally
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([
authInterceptor,
loggingInterceptor,
errorInterceptor,
])
),
],
};
APP_INITIALIZER
Run async code before app starts:
import { APP_INITIALIZER } from '@angular/core';
function initializeApp(configService: ConfigService): () => Promise<void> {
return () => configService.loadConfig();
}
export const appConfig: ApplicationConfig = {
providers: [
ConfigService,
{
provide: APP_INITIALIZER,
useFactory: initializeApp,
deps: [ConfigService],
multi: true,
},
],
};
Multiple Initializers
providers: [
{
provide: APP_INITIALIZER,
useFactory: (config: ConfigService) => () => config.load(),
deps: [ConfigService],
multi: true,
},
{
provide: APP_INITIALIZER,
useFactory: (auth: AuthService) => () => auth.checkSession(),
deps: [AuthService],
multi: true,
},
]
Environment Injector
Create injectors programmatically:
import { createEnvironmentInjector, EnvironmentInjector, inject } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class PluginService {
private parentInjector = inject(EnvironmentInjector);
loadPlugin(providers: Provider[]): EnvironmentInjector {
return createEnvironmentInjector(providers, this.parentInjector);
}
}
runInInjectionContext
Run code with injection context:
import { runInInjectionContext, EnvironmentInjector, inject } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class UtilityService {
private injector = inject(EnvironmentInjector);
executeWithDI<T>(fn: () => T): T {
return runInInjectionContext(this.injector, fn);
}
}
// Usage
utilityService.executeWithDI(() => {
const http = inject(HttpClient);
// Use http...
});
For advanced patterns, see references/di-patterns.md.
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.