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
async loadUsers() {
const users = await firstValueFrom(
this.http.get
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
// Object token export interface AppConfig { apiUrl: string; features: { darkMode: boolean; analytics: boolean; }; }
export const APP_CONFIG = new InjectionToken
// Token with factory (self-providing)
export const WINDOW = new InjectionToken
export const LOCAL_STORAGE = new InjectionToken
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
// Conditional implementation { provide: LoggerService, useClass: environment.production ? ProductionLoggerService : ConsoleLoggerService, }
useValue // Static values
// 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
// 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
// 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
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
// Usage utilityService.executeWithDI(() => { const http = inject(HttpClient); // Use http... });
For advanced patterns, see references/di-patterns.md.