angular-di

安装量: 2.9K
排名: #764

安装

npx skills add https://github.com/analogjs/angular-skills --skill angular-di

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([]); readonly users$ = this.users.asReadonly();

async loadUsers() { const users = await firstValueFrom( this.http.get('/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('API_URL');

// Object token export interface AppConfig { apiUrl: string; features: { darkMode: boolean; analytics: boolean; }; }

export const APP_CONFIG = new InjectionToken('APP_CONFIG');

// Token with factory (self-providing) export const WINDOW = new InjectionToken('Window', { providedIn: 'root', factory: () => window, });

export const LOCAL_STORAGE = new InjectionToken('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

// 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('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 { 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(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.

返回排行榜