angular.courses

Constructor Injection Patterns

How constructor-based dependency injection works: private readonly, @Optional, @Self, InjectionToken, and multi providers

Overview

Constructor injection is Angular's traditional way to supply dependencies: the framework calls your constructor with the requested services. This practice covers how it works and common patterns: private readonly parameters, @Optional(), @Self(), @SkipSelf(), @Host(), InjectionToken, and multi providers. For the evolution to inject(), see the related practice.

The Evolution

v2Constructor Injection

Dependencies declared as constructor parameters; Angular injects them at creation time.

v14inject()

inject() offers an alternative; constructor injection remains fully supported.

Patterns

Basic constructor injection

Constructor parametersBefore v17
@Component({ ... })
export class DashboardComponent {
constructor(
private userService: UserService,
private router: Router,
private route: ActivatedRoute
) {}
}
Same with inject()v14+
@Component({ ... })
export class DashboardComponent {
private userService = inject(UserService);
private router = inject(Router);
private route = inject(ActivatedRoute);
}

private readonly pattern

private readonlyBefore v17
constructor(
private readonly userService: UserService,
private readonly logger: LoggerService
) {}
// readonly prevents reassignment; private keeps it off template
Same intent with inject()v14+
private readonly userService = inject(UserService);
private readonly logger = inject(LoggerService);

Optional injection: @Optional()

@Optional() in constructorBefore v17
constructor(
private config: AppConfig,
@Optional() private analytics: AnalyticsService
) {
if (this.analytics) {
this.analytics.track('init');
}
}
inject() with optionalv14+
private config = inject(AppConfig);
private analytics = inject(AnalyticsService, { optional: true });
constructor() {
if (this.analytics) {
this.analytics.track('init');
}
}

InjectionToken and multi providers

InjectionToken in constructorBefore v17
const API_URL = new InjectionToken<string>('API_URL');
@Component({ ... })
export class ApiComponent {
constructor(@Inject(API_URL) private apiUrl: string) {}
}
inject(InjectionToken)v14+
const API_URL = new InjectionToken<string>('API_URL');
@Component({ ... })
export class ApiComponent {
private apiUrl = inject(API_URL);
}

Hierarchy: @Self, @SkipSelf, @Host

@Self() and @SkipSelf()Before v17
// @Self(): only look in the component's own injector
// @SkipSelf(): skip component injector, use parent
constructor(
@Self() private localConfig: ConfigService,
@SkipSelf() private parentLogger: LoggerService
) {}
inject() with self/skipSelfv14+
private localConfig = inject(ConfigService, { self: true });
private parentLogger = inject(LoggerService, { skipSelf: true });

Key Differences

Declaration:Constructor parametersinject() in field initializer (see related practice)
Optional:@Optional()inject(..., { optional: true })
Token:@Inject(TOKEN)inject(TOKEN)
Hierarchy:@Self(), @SkipSelf(), @Host()inject(..., { self: true }), etc.

Benefits

Explicit

Dependencies listed in one place

Testable

Override in tests via TestBed

Type-safe

TypeScript types for tokens

Optional/multi

@Optional(), multi providers

Hierarchy

@Self, @SkipSelf, @Host for scope

When to use constructor injection

| Scenario | Pattern | |----------|---------| | Required service | constructor(private svc: MyService) or inject(MyService) | | Optional service | @Optional() private svc or inject(MyService, { optional: true }) | | InjectionToken | @Inject(TOKEN) or inject(TOKEN) | | Only self injector | @Self() or inject(..., { self: true }) | | Skip self (parent only) | @SkipSelf() or inject(..., { skipSelf: true }) | | Multi providers | inject(TOKEN) returns array when multi: true |

Prefer inject() for new code

Constructor injection is still valid. For new code, prefer inject() so you can use it in field initializers and outside the constructor (e.g. in guards, resolvers). See the related practice "From Constructor Injection to inject()".

Related Evolutions