Angular Advanced DI
Advanced DI uses hierarchical injectors, custom InjectionToken
, optional and multi providers, and function-style inject()
to compose flexible configurations.
Advanced DI Essentials
- Scope: Hierarchical injectors mean providers at app/route/component levels define where instances live.
- Tokens: Use
InjectionToken
for non-class values and configuration. - Function inject: Use
inject()
in constructors or utility functions. - Optional & Multi: Optional dependencies and multi providers enable flexible composition.
import { InjectionToken, inject, Optional } from '@angular/core';
// Tokens
export const CONFIG = new InjectionToken<{ api: string }>('CONFIG');
export const FEATURES = new InjectionToken<string[]>('FEATURES');
// Optional inject
const cfg = inject(CONFIG, { optional: true });
// Multi providers
bootstrapApplication(App, {
providers: [
{ provide: CONFIG, useValue: { api: '/api' } },
{ provide: FEATURES, useValue: 'search', multi: true },
{ provide: FEATURES, useValue: 'share', multi: true },
]
});
Code explained
- InjectionToken: Defines a typed token for non-class values and configuration.
- inject(TOKEN, { optional: true }): Retrieves a dependency and returns
null
/undefined
when missing. - multi: true: Collects multiple provider values into an array for the same token.
Notes:
- Related: See Services & DI, Router, and App Bootstrap.
- Use
InjectionToken
for configuration, Use function-styleinject()
in utilities, and scope providers to routes/components when possible.
Advanced DI Example
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component, InjectionToken, inject } from '@angular/core';
const FEATURES = new InjectionToken<string[]>('FEATURES');
@Component({
selector: 'app-root',
standalone: true,
template: `
<h3>DI: Multi Providers</h3>
<p>Features: {{ features.join(', ') }}</p>
`
})
class App {
features = inject(FEATURES);
}
bootstrapApplication(App, {
providers: [
{ provide: FEATURES, useValue: 'search', multi: true },
{ provide: FEATURES, useValue: 'share', multi: true },
{ provide: FEATURES, useValue: 'ai', multi: true }
]
});
<app-root></app-root>
Example explained
- FEATURES token: A multi provider token that aggregates strings contributed by providers.
- inject(FEATURES): Reads the aggregated array at runtime.
- Display: The template joins the features into a comma-separated list.
Notes:
- Tokens over types: Use
InjectionToken
for values and interfaces, not classes. - Multi providers: Add
multi: true
to collect many contributions under one token. - Scope providers: Use providing at routes/components instead of root when you need isolation.
Optional and Multi Providers
- Use
@Optional()
orinject(TOKEN, { optional: true })
when a dependency is not required. - Multi providers let many values contribute to the same token.
import { InjectionToken, inject, Optional } from '@angular/core';
export const USER_NAME = new InjectionToken<string>('USER_NAME');
// Constructor optional
constructor(@Optional() public maybeName?: string) {}
// Function optional
const name = inject(USER_NAME, { optional: true });
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component, InjectionToken, inject } from '@angular/core';
const USER_NAME = new InjectionToken<string>('USER_NAME');
const FEATURES = new InjectionToken<string[]>('FEATURES');
@Component({
selector: 'app-root',
standalone: true,
template: `
<h3>Optional & Multi</h3>
<p>Hello: {{ name || 'Anonymous' }}</p>
<p>Features: {{ features?.join(', ') || '∅' }}</p>
`
})
class App {
name = inject(USER_NAME, { optional: true });
features = inject(FEATURES, { optional: true });
}
bootstrapApplication(App, {
providers: [
// Try commenting USER_NAME to see optional behavior
{ provide: USER_NAME, useValue: 'Dana' },
{ provide: FEATURES, useValue: 'search', multi: true },
{ provide: FEATURES, useValue: 'share', multi: true },
]
});
<app-root></app-root>
Example explained
- Optional inject:
inject(USER_NAME, { optional: true })
returns a value when provided ornull/undefined
when absent. - Multi providers: Multiple
FEATURES
entries are combined into an array. - Fallbacks: The template shows defaults when optional values are missing.
Notes:
- Optional deps: Always handle
null
when a dependency is optional. - Avoid collisions: Name tokens clearly to prevent accidental reuse across features.
- Config pattern: Model global settings with tokens and override per route/component as needed.