Angular's dependency injection system is powerful and flexible, enabling loosely coupled and testable code.
Basic Injection
typescript
@Injectable({ providedIn: 'root' })
export class UserService {
constructor(private http: HttpClient) {}
getUsers() {
return this.http.get<User[]>('/api/users');
}
}
@Component({
selector: 'app-users',
template: '...'
})
export class UsersComponent {
constructor(private userService: UserService) {}
}Providing Services
Root Level
typescript
@Injectable({ providedIn: 'root' })
export class DataService {
constructor() {}
}Component Level
typescript
@Component({
selector: 'app-user',
providers: [UserService],
template: '...'
})
export class UserComponent {
constructor(private userService: UserService) {}
}Module Level
typescript
@NgModule({
providers: [UserService]
})
export class UserModule {}Injection Tokens
typescript
import { InjectionToken } from '@angular/core';
export const API_URL = new InjectionToken<string>('api-url');
// Provide
providers: [
{ provide: API_URL, useValue: 'https://api.example.com' }
]
// Inject
constructor(@Inject(API_URL) private apiUrl: string) {}Providers
useValue
typescript
export const CONFIG = {
apiUrl: 'https://api.example.com',
timeout: 5000
};
providers: [
{ provide: 'CONFIG', useValue: CONFIG }
]useClass
typescript
export class LoggerService {
log(message: string) {
console.log(message);
}
}
export class ConsoleLoggerService extends LoggerService {
log(message: string) {
console.log('[CONSOLE]', message);
}
}
providers: [
{ provide: LoggerService, useClass: ConsoleLoggerService }
]useExisting
typescript
providers: [
{ provide: LoggerService, useClass: LoggerService },
{ provide: 'LOGGER', useExisting: LoggerService }
]useFactory
typescript
export function loggerFactory(config: Config) {
return config.production
? new ProductionLogger()
: new DevelopmentLogger();
}
providers: [
{ provide: Config, useValue: CONFIG },
{ provide: LoggerService, useFactory: loggerFactory, deps: [Config] }
]Optional Dependencies
typescript
constructor(
@Optional() private logger?: LoggerService
) {}@Self, @SkipSelf, @Host
typescript
constructor(
@Self() private selfService: Service,
@SkipSelf() private parentService: Service,
@Host() private hostService: Service
) {}Inject Decorator
typescript
import { Inject } from '@angular/core';
constructor(
@Inject(API_URL) private apiUrl: string,
@Inject(LOCALE_ID) private locale: string
) {}inject() Function
typescript
export class MyComponent {
private userService = inject(UserService);
private router = inject(Router);
constructor() {
this.userService.getUsers().subscribe();
}
}Injection in Functions and Pipes
typescript
export function loadData() {
const http = inject(HttpClient);
return http.get('/api/data');
}
@Pipe({ name: 'format', standalone: true })
export class FormatPipe implements PipeTransform {
private config = inject(Config);
transform(value: string): string {
return this.config.prefix + value;
}
}Custom Injection Tokens
typescript
import { InjectionToken, inject } from '@angular/core';
interface AppConfig {
apiUrl: string;
environment: string;
}
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');
// In providers
{
provide: APP_CONFIG,
useValue: {
apiUrl: 'https://api.example.com',
environment: 'production'
}
}
// In component
const config = inject(APP_CONFIG);Tree-Shakeable Providers
typescript
@Injectable({ providedIn: 'root' })
export class FeatureService {
// Service is tree-shakeable
}Testing with DI
typescript
describe('UserComponent', () => {
let component: UserComponent;
let fixture: ComponentFixture<UserComponent>;
let mockUserService: jasmine.SpyObj<UserService>;
beforeEach(async () => {
mockUserService = jasmine.createSpyObj('UserService', ['getUsers']);
await TestBed.configureTestingModule({
declarations: [UserComponent],
providers: [
{ provide: UserService, useValue: mockUserService }
]
}).compileComponents();
fixture = TestBed.createComponent(UserComponent);
component = fixture.componentInstance;
});
it('should load users', () => {
mockUserService.getUsers.and.returnValue(of([]));
fixture.detectChanges();
expect(mockUserService.getUsers).toHaveBeenCalled();
});
});Angular's DI system provides flexible and powerful dependency management.