mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
docs: update dependency injection guides (part 1) (#63652)
PR Close #63652
This commit is contained in:
parent
5c5353d1e5
commit
f321e226bd
10 changed files with 1211 additions and 454 deletions
|
|
@ -13,6 +13,18 @@ import {Route} from '@angular/router';
|
|||
*/
|
||||
|
||||
export const REDIRECT_ROUTES: Route[] = [
|
||||
{
|
||||
path: 'guide/di/dependency-injection',
|
||||
redirectTo: 'guide/di',
|
||||
},
|
||||
{
|
||||
path: 'guide/di/creating-injectable-service',
|
||||
redirectTo: 'guide/di/creating-and-using-services',
|
||||
},
|
||||
{
|
||||
path: 'guide/di/dependency-injection-providers',
|
||||
redirectTo: 'guide/di/defining-dependency-providers',
|
||||
},
|
||||
{
|
||||
path: 'guide/defer',
|
||||
redirectTo: '/guide/templates/defer',
|
||||
|
|
|
|||
|
|
@ -287,26 +287,25 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
|
|||
},
|
||||
{
|
||||
label: 'Dependency Injection',
|
||||
status: 'updated',
|
||||
children: [
|
||||
{
|
||||
label: 'Overview',
|
||||
path: 'guide/di',
|
||||
contentPath: 'guide/di/overview',
|
||||
status: 'updated',
|
||||
},
|
||||
{
|
||||
label: 'Understanding dependency injection',
|
||||
path: 'guide/di/dependency-injection',
|
||||
contentPath: 'guide/di/dependency-injection',
|
||||
},
|
||||
{
|
||||
label: 'Creating an injectable service',
|
||||
path: 'guide/di/creating-injectable-service',
|
||||
contentPath: 'guide/di/creating-injectable-service',
|
||||
label: 'Creating and using services',
|
||||
path: 'guide/di/creating-and-using-services',
|
||||
contentPath: 'guide/di/creating-and-using-services',
|
||||
status: 'updated',
|
||||
},
|
||||
{
|
||||
label: 'Defining dependency providers',
|
||||
path: 'guide/di/dependency-injection-providers',
|
||||
contentPath: 'guide/di/dependency-injection-providers',
|
||||
path: 'guide/di/defining-dependency-providers',
|
||||
contentPath: 'guide/di/defining-dependency-providers',
|
||||
status: 'updated',
|
||||
},
|
||||
{
|
||||
label: 'Injection context',
|
||||
|
|
|
|||
105
adev/src/content/guide/di/creating-and-using-services.md
Normal file
105
adev/src/content/guide/di/creating-and-using-services.md
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
# Creating and using services
|
||||
|
||||
Services are reusable pieces of code that can be shared across your Angular application. They typically handle data fetching, business logic, or other functionality that multiple components need to access.
|
||||
|
||||
## Creating a service
|
||||
|
||||
You can create a service with the [Angular CLI](tools/cli) with the following command:
|
||||
|
||||
```bash
|
||||
ng generate service CUSTOM_NAME
|
||||
```
|
||||
|
||||
This creates a dedicated `CUSTOM_NAME.ts` file in your `src` directory.
|
||||
|
||||
You can also manually create a service by adding the `@Injectable()` decorator to a TypeScript class. This tells Angular that the service can be injected as a dependency.
|
||||
|
||||
Here is an example of a service that allows users to add and request data:
|
||||
|
||||
```ts
|
||||
// 📄 src/app/basic-data-store.ts
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class BasicDataStore {
|
||||
private data: string[] = []
|
||||
|
||||
addData(item: string): void {
|
||||
this.data.push(item)
|
||||
}
|
||||
|
||||
getData(): string[] {
|
||||
return [...this.data]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## How services become available
|
||||
|
||||
When you use `@Injectable({ providedIn: 'root' })` in your service, Angular:
|
||||
|
||||
- **Creates a single instance** (singleton) for your entire application
|
||||
- **Makes it available everywhere** without any additional configuration
|
||||
- **Enables tree-shaking** so the service is only included in your JavaScript bundle if it's actually used
|
||||
|
||||
This is the recommended approach for most services.
|
||||
|
||||
## Injecting a service
|
||||
|
||||
Once you've created a service with `providedIn: 'root'`, you can inject it anywhere in your application using the `inject()` function from `@angular/core`.
|
||||
|
||||
### Injecting into a component
|
||||
|
||||
```angular-ts
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { BasicDataStore } from './basic-data-store';
|
||||
|
||||
@Component({
|
||||
selector: 'app-example',
|
||||
template: `
|
||||
<div>
|
||||
<p>{{ dataStore.getData() }}</p>
|
||||
<button (click)="dataStore.addData('More data')">
|
||||
Add more data
|
||||
</button>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export class ExampleComponent {
|
||||
dataStore = inject(BasicDataStore);
|
||||
}
|
||||
```
|
||||
|
||||
### Injecting into another service
|
||||
|
||||
```ts
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { AdvancedDataStore } from './advanced-data-store';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class BasicDataStore {
|
||||
private advancedDataStore = inject(AdvancedDataStore);
|
||||
private data: string[] = [];
|
||||
|
||||
addData(item: string): void {
|
||||
this.data.push(item);
|
||||
}
|
||||
|
||||
getData(): string[] {
|
||||
return [...this.data, ...this.advancedDataStore.getData()];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Next steps
|
||||
|
||||
While `providedIn: 'root'` covers most use cases, Angular offers additional ways to provide services for specialized scenarios:
|
||||
|
||||
- **Component-specific instances** - When components need their own isolated service instances
|
||||
- **Manual configuration** - For services that require runtime configuration
|
||||
- **Factory providers** - For dynamic service creation based on runtime conditions
|
||||
- **Value providers** - For providing configuration objects or constants
|
||||
|
||||
You can learn more about these advanced patterns in the next guide: [defining dependency providers](/guide/di/defining-dependency-providers).
|
||||
919
adev/src/content/guide/di/defining-dependency-providers.md
Normal file
919
adev/src/content/guide/di/defining-dependency-providers.md
Normal file
|
|
@ -0,0 +1,919 @@
|
|||
# Defining dependency providers
|
||||
|
||||
Angular provides two ways to make services available for injection:
|
||||
|
||||
1. **Automatic provision** - Using `providedIn` in the `@Injectable` decorator or by providing a factory in the `InjectionToken` configuration
|
||||
2. **Manual provision** - Using the `providers` array in components, directives, routes, or application config
|
||||
|
||||
In the [previous guide](/guide/di/creating-and-using-services), you learned how to create services using `providedIn: 'root'`, which handles most common use cases. This guide explores additional patterns for both automatic and manual provider configuration.
|
||||
|
||||
## Automatic provision for non-class dependencies
|
||||
|
||||
While the `@Injectable` decorator with `providedIn: 'root'` works great for services (classes), you might need to provide other types of values globally - like configuration objects, functions, or primitive values. Angular provides `InjectionToken` for this purpose.
|
||||
|
||||
### What is an InjectionToken?
|
||||
|
||||
An `InjectionToken` is an object that Angular's dependency injection system uses to uniquely identify values for injection. Think of it as a special key that lets you store and retrieve any type of value in Angular's DI system:
|
||||
|
||||
```ts
|
||||
import { InjectionToken } from '@angular/core';
|
||||
|
||||
// Create a token for a string value
|
||||
export const API_URL = new InjectionToken<string>('api.url');
|
||||
|
||||
// Create a token for a function
|
||||
export const LOGGER = new InjectionToken<(msg: string) => void>('logger.function');
|
||||
|
||||
// Create a token for a complex type
|
||||
export interface Config {
|
||||
apiUrl: string;
|
||||
timeout: number;
|
||||
}
|
||||
export const CONFIG_TOKEN = new InjectionToken<Config>('app.config');
|
||||
```
|
||||
|
||||
NOTE: The string parameter (e.g., `'api.url'`) is a description purely for debugging — Angular identifies tokens by their object reference, not this string.
|
||||
|
||||
### InjectionToken with `providedIn: 'root'`
|
||||
|
||||
An `InjectionToken` that has a `factory` results in `providedIn: 'root'` by default (but can be overidden via the `providedIn` prop).
|
||||
|
||||
```ts
|
||||
// 📁 /app/config.token.ts
|
||||
import { InjectionToken } from '@angular/core';
|
||||
|
||||
export interface AppConfig {
|
||||
apiUrl: string;
|
||||
version: string;
|
||||
features: Record<string, boolean>;
|
||||
}
|
||||
|
||||
// Globally available configuration using providedIn
|
||||
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config', {
|
||||
providedIn: 'root',
|
||||
factory: () => ({
|
||||
apiUrl: 'https://api.example.com',
|
||||
version: '1.0.0',
|
||||
features: {
|
||||
darkMode: true,
|
||||
analytics: false
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// No need to add to providers array - available everywhere!
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
template: `<h1>Version: {{ config.version }}</h1>`
|
||||
})
|
||||
export class HeaderComponent {
|
||||
config = inject(APP_CONFIG); // Automatically available
|
||||
}
|
||||
```
|
||||
|
||||
### When to use InjectionToken with factory functions
|
||||
|
||||
InjectionToken with factory functions is ideal when you can't use a class but need to provide dependencies globally:
|
||||
|
||||
```ts
|
||||
// 📁 /app/logger.token.ts
|
||||
import { InjectionToken, inject } from '@angular/core';
|
||||
import { APP_CONFIG } from './config.token';
|
||||
|
||||
// Logger function type
|
||||
export type LoggerFn = (level: string, message: string) => void;
|
||||
|
||||
// Global logger function with dependencies
|
||||
export const LOGGER_FN = new InjectionToken<LoggerFn>('logger.function', {
|
||||
providedIn: 'root',
|
||||
factory: () => {
|
||||
const config = inject(APP_CONFIG);
|
||||
|
||||
return (level: string, message: string) => {
|
||||
if (config.features.logging !== false) {
|
||||
console[level](`[${new Date().toISOString()}] ${message}`);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 📁 /app/storage.token.ts
|
||||
// Providing browser APIs as tokens
|
||||
export const LOCAL_STORAGE = new InjectionToken<Storage>('localStorage', {
|
||||
// providedIn: 'root' is configured as the default
|
||||
factory: () => window.localStorage
|
||||
});
|
||||
|
||||
export const SESSION_STORAGE = new InjectionToken<Storage>('sessionStorage', {
|
||||
providedIn: 'root',
|
||||
factory: () => window.sessionStorage
|
||||
});
|
||||
|
||||
// 📁 /app/feature-flags.token.ts
|
||||
// Complex configuration with runtime logic
|
||||
export const FEATURE_FLAGS = new InjectionToken<Map<string, boolean>>('feature.flags', {
|
||||
providedIn: 'root',
|
||||
factory: () => {
|
||||
const flags = new Map<string, boolean>();
|
||||
|
||||
// Parse from environment or URL params
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const enableBeta = urlParams.get('beta') === 'true';
|
||||
|
||||
flags.set('betaFeatures', enableBeta);
|
||||
flags.set('darkMode', true);
|
||||
flags.set('newDashboard', false);
|
||||
|
||||
return flags;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
This approach offers several advantages:
|
||||
|
||||
- **No manual provider configuration needed** - Works just like `providedIn: 'root'` for services
|
||||
- **Tree-shakeable** - Only included if actually used
|
||||
- **Type-safe** - Full TypeScript support for non-class values
|
||||
- **Can inject other dependencies** - Factory functions can use `inject()` to access other services
|
||||
|
||||
## Understanding manual provider configuration
|
||||
|
||||
When you need more control than `providedIn: 'root'` offers, you can manually configure providers. Manual configuration through the `providers` array is useful when:
|
||||
|
||||
1. **The service doesn't have `providedIn`** - Services without automatic provision must be manually provided
|
||||
2. **You want a new instance** - To create a separate instance at the component/directive level instead of using the shared one
|
||||
3. **You need runtime configuration** - When service behavior depends on runtime values
|
||||
4. **You're providing non-class values** - Configuration objects, functions, or primitive values
|
||||
|
||||
### Example: Service without `providedIn`
|
||||
|
||||
```ts
|
||||
import { Injectable, Component, inject } from '@angular/core';
|
||||
|
||||
// Service without providedIn
|
||||
@Injectable()
|
||||
export class LocalDataStore {
|
||||
private data: string[] = [];
|
||||
|
||||
addData(item: string) {
|
||||
this.data.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
// Component must provide it
|
||||
@Component({
|
||||
selector: 'app-example',
|
||||
// A provider is required here because the `LocalDataStore` service has no providedIn.
|
||||
providers: [LocalDataStore],
|
||||
template: `...`
|
||||
})
|
||||
export class ExampleComponent {
|
||||
dataStore = inject(LocalDataStore);
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Creating component-specific instances
|
||||
|
||||
Services with `providedIn: 'root'` can be overridden at the component level. This ties the instance of the service to the life of a component. As a result, when the component gets destroyed, the provided service is also destroyed as well.
|
||||
|
||||
```ts
|
||||
import { Injectable, Component, inject } from '@angular/core';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class DataStore {
|
||||
private data: ListItem[] = [];
|
||||
}
|
||||
|
||||
// This component gets its own instance
|
||||
@Component({
|
||||
selector: 'app-isolated',
|
||||
// Creates new instance of `DataStore` rather than using the root-provided instance.
|
||||
providers: [DataStore],
|
||||
template: `...`
|
||||
})
|
||||
export class IsolatedComponent {
|
||||
dataStore = inject(DataStore); // Component-specific instance
|
||||
}
|
||||
```
|
||||
|
||||
## Injector hierarchy in Angular
|
||||
|
||||
Angular's dependency injection system is hierarchical. When a component requests a dependency, Angular starts with that component's injector and walks up the tree until it finds a provider for that dependency. Each component in your application tree can have its own injector, and these injectors form a hierarchy that mirrors your component tree.
|
||||
|
||||
This hierarchy enables:
|
||||
|
||||
- **Scoped instances**: Different parts of your app can have different instances of the same service
|
||||
- **Override behavior**: Child components can override providers from parent components
|
||||
- **Memory efficiency**: Services are only instantiated where needed
|
||||
|
||||
In Angular, any element with a component or directive can provide values to all of its descendants.
|
||||
|
||||
`mermaid
|
||||
graph TD
|
||||
subgraph platform
|
||||
subgraph root
|
||||
direction TB
|
||||
A[SocialApp] --> B[UserProfile]
|
||||
A --> C[FriendList]
|
||||
C --> D[FriendEntry]
|
||||
end
|
||||
end
|
||||
`
|
||||
|
||||
In the example above:
|
||||
|
||||
1. `SocialApp` can provide values for `UserProfile` and `FriendList`
|
||||
2. `FriendList` can provide values for injection to `FriendEntry`, but cannot provide values for injection in `UserProfile` because it's not part of the tree
|
||||
|
||||
## Declaring a provider
|
||||
|
||||
Think of Angular's dependency injection system as a hash map or dictionary. Each provider configuration object defines a key-value pair:
|
||||
|
||||
- **Key (Provider identifier)**: The unique identifier you use to request a dependency
|
||||
- **Value**: What Angular should return when that token is requested
|
||||
|
||||
When manually providing dependencies, you typically see this shorthand syntax:
|
||||
|
||||
```angular-ts
|
||||
import { Component } from '@angular/core';
|
||||
import { LocalService } from './local-service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-example',
|
||||
providers: [LocalService] // Service without providedIn
|
||||
})
|
||||
export class ExampleComponent { }
|
||||
```
|
||||
|
||||
This is actually a shorthand for a more detailed provider configuration:
|
||||
|
||||
```ts
|
||||
{
|
||||
// This is the shorthand version
|
||||
providers: [LocalService],
|
||||
|
||||
// This is the full version
|
||||
providers: [
|
||||
{ provide: LocalService, useClass: LocalService }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Provider configuration object
|
||||
|
||||
Every provider configuration object has two primary parts:
|
||||
|
||||
1. **Provider identifier**: The unique key that Angular uses to get the dependency (set via the `provide` property)
|
||||
2. **Value**: The actual dependency that you want Angular to fetch, configured with different keys based on the desired type:
|
||||
- `useClass` - Provides a JavaScript class
|
||||
- `useValue` - Provides a static value
|
||||
- `useFactory` - Provides a factory function that returns the value
|
||||
- `useExisting` - Provides an alias to an existing provider
|
||||
|
||||
### Provider identifiers
|
||||
|
||||
Provider identifiers allow Angular's dependency injection (DI) system to retrieve a dependency through a unique ID. You can generate provider identifiers in two ways:
|
||||
|
||||
1. [Class names](#class-names)
|
||||
2. [Injection tokens](#injection-tokens)
|
||||
|
||||
#### Class names
|
||||
|
||||
Class name use the imported class directly as the identifier:
|
||||
|
||||
```angular-ts
|
||||
import { Component } from '@angular/core';
|
||||
import { LocalService } from './local-service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-example',
|
||||
providers: [
|
||||
{ provide: LocalService, useClass: LocalService }
|
||||
]
|
||||
})
|
||||
export class ExampleComponent { /* ... */ }
|
||||
```
|
||||
|
||||
The class serves as both the identifier and the implementation, which is why Angular provides the shorthand `providers: [LocalService]`.
|
||||
|
||||
#### Injection tokens
|
||||
|
||||
Angular provides a built-in [`InjectionToken`](api/core/InjectionToken) class that creates a unique object reference for injectable values or when you want to provide multiple implementations of the same interface.
|
||||
|
||||
```ts
|
||||
// 📁 /app/tokens.ts
|
||||
import { InjectionToken } from '@angular/core';
|
||||
import { DataService } from './data-service.interface';
|
||||
|
||||
export const DATA_SERVICE_TOKEN = new InjectionToken<DataService>('DataService');
|
||||
```
|
||||
|
||||
NOTE: The string `'DataService'` is a description used purely for debugging purposes. Angular identifies the token by its object reference, not this string.
|
||||
|
||||
Use the token in your provider configuration:
|
||||
|
||||
```angular-ts
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { LocalDataService } from './local-data-service';
|
||||
import { DATA_SERVICE_TOKEN } from './tokens';
|
||||
|
||||
@Component({
|
||||
selector: 'app-example',
|
||||
providers: [
|
||||
{ provide: DATA_SERVICE_TOKEN, useClass: LocalDataService }
|
||||
]
|
||||
})
|
||||
export class ExampleComponent {
|
||||
private dataService = inject(DATA_SERVICE_TOKEN);
|
||||
}
|
||||
```
|
||||
|
||||
#### Can TypeScript interfaces be identifiers for injection?
|
||||
|
||||
TypeScript interfaces cannot be used for injection because they don't exist at runtime:
|
||||
|
||||
```ts
|
||||
// ❌ This won't work!
|
||||
interface DataService {
|
||||
getData(): string[];
|
||||
}
|
||||
|
||||
// Interfaces disappear after TypeScript compilation
|
||||
@Component({
|
||||
providers: [
|
||||
{ provide: DataService, useClass: LocalDataService } // Error!
|
||||
]
|
||||
})
|
||||
export class ExampleComponent {
|
||||
private dataService = inject(DataService); // Error!
|
||||
}
|
||||
|
||||
// ✅ Use InjectionToken instead
|
||||
export const DATA_SERVICE_TOKEN = new InjectionToken<DataService>('DataService');
|
||||
|
||||
@Component({
|
||||
providers: [
|
||||
{ provide: DATA_SERVICE_TOKEN, useClass: LocalDataService }
|
||||
]
|
||||
})
|
||||
export class ExampleComponent {
|
||||
private dataService = inject(DATA_SERVICE_TOKEN); // Works!
|
||||
}
|
||||
```
|
||||
|
||||
The InjectionToken provides a runtime value that Angular's DI system can use, while still maintaining type safety through TypeScript's generic type parameter.
|
||||
|
||||
### Provider value types
|
||||
|
||||
#### useClass
|
||||
|
||||
`useClass` provides a JavaScript class as a dependency. This is the default when using the shorthand syntax:
|
||||
|
||||
```ts
|
||||
// Shorthand
|
||||
providers: [DataService]
|
||||
|
||||
// Full syntax
|
||||
providers: [
|
||||
{ provide: DataService, useClass: DataService }
|
||||
]
|
||||
|
||||
// Different implementation
|
||||
providers: [
|
||||
{ provide: DataService, useClass: MockDataService }
|
||||
]
|
||||
|
||||
// Conditional implementation
|
||||
providers: [
|
||||
{
|
||||
provide: StorageService,
|
||||
useClass: environment.production ? CloudStorageService : LocalStorageService
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### Practical example: Logger substitution
|
||||
|
||||
You can substitute implementations to extend functionality:
|
||||
|
||||
```ts
|
||||
import { Injectable, Component, inject } from '@angular/core';
|
||||
|
||||
// Base logger
|
||||
@Injectable()
|
||||
export class Logger {
|
||||
log(message: string) {
|
||||
console.log(message);
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced logger with timestamp
|
||||
@Injectable()
|
||||
export class BetterLogger extends Logger {
|
||||
override log(message: string) {
|
||||
super.log(`[${new Date().toISOString()}] ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Logger that includes user context
|
||||
@Injectable()
|
||||
export class EvenBetterLogger extends Logger {
|
||||
private userService = inject(UserService);
|
||||
|
||||
override log(message: string) {
|
||||
const name = this.userService.user.name;
|
||||
super.log(`Message to ${name}: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// In your component
|
||||
@Component({
|
||||
selector: 'app-example',
|
||||
providers: [
|
||||
UserService, // EvenBetterLogger needs this
|
||||
{ provide: Logger, useClass: EvenBetterLogger }
|
||||
]
|
||||
})
|
||||
export class ExampleComponent {
|
||||
private logger = inject(Logger); // Gets EvenBetterLogger instance
|
||||
}
|
||||
```
|
||||
|
||||
#### useValue
|
||||
|
||||
`useValue` provides any JavaScript data type as a static value:
|
||||
|
||||
```ts
|
||||
providers: [
|
||||
{ provide: API_URL_TOKEN, useValue: 'https://api.example.com' },
|
||||
{ provide: MAX_RETRIES_TOKEN, useValue: 3 },
|
||||
{ provide: FEATURE_FLAGS_TOKEN, useValue: { darkMode: true, beta: false } }
|
||||
]
|
||||
```
|
||||
|
||||
IMPORTANT: TypeScript types and interfaces cannot serve as dependency values. They exist only at compile-time.
|
||||
|
||||
#### Practical example: Application configuration
|
||||
|
||||
A common use case for `useValue` is providing application configuration:
|
||||
|
||||
```ts
|
||||
// Define configuration interface
|
||||
export interface AppConfig {
|
||||
apiUrl: string;
|
||||
appTitle: string;
|
||||
features: {
|
||||
darkMode: boolean;
|
||||
analytics: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
// Create injection token
|
||||
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');
|
||||
|
||||
// Define configuration
|
||||
const appConfig: AppConfig = {
|
||||
apiUrl: 'https://api.example.com',
|
||||
appTitle: 'My Application',
|
||||
features: {
|
||||
darkMode: true,
|
||||
analytics: false
|
||||
}
|
||||
};
|
||||
|
||||
// Provide in bootstrap
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [
|
||||
{ provide: APP_CONFIG, useValue: appConfig }
|
||||
]
|
||||
});
|
||||
|
||||
// Use in component
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
template: `<h1>{{ title }}</h1>`
|
||||
})
|
||||
export class HeaderComponent {
|
||||
private config = inject(APP_CONFIG);
|
||||
title = this.config.appTitle;
|
||||
}
|
||||
```
|
||||
|
||||
#### useFactory
|
||||
|
||||
`useFactory` provides a function that generates a new value for injection:
|
||||
|
||||
```ts
|
||||
export const loggerFactory = (config: AppConfig) => {
|
||||
return new LoggerService(config.logLevel, config.endpoint);
|
||||
};
|
||||
|
||||
providers: [
|
||||
{
|
||||
provide: LoggerService,
|
||||
useFactory: loggerFactory,
|
||||
deps: [APP_CONFIG] // Dependencies for the factory function
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
You can mark factory dependencies as optional:
|
||||
|
||||
```ts
|
||||
import { Optional } from '@angular/core';
|
||||
|
||||
providers: [
|
||||
{
|
||||
provide: MyService,
|
||||
useFactory: (required: RequiredService, optional?: OptionalService) => {
|
||||
return new MyService(required, optional || new DefaultService());
|
||||
},
|
||||
deps: [RequiredService, [new Optional(), OptionalService]]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### Practical example: Configuration-based API client
|
||||
|
||||
Here's a complete example showing how to use a factory to create a service with runtime configuration:
|
||||
|
||||
```ts
|
||||
// Service that needs runtime configuration
|
||||
class ApiClient {
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private baseUrl: string,
|
||||
private rateLimitMs: number
|
||||
) {}
|
||||
|
||||
async fetchData(endpoint: string) {
|
||||
// Apply rate limiting based on user tier
|
||||
await this.applyRateLimit();
|
||||
return this.http.get(`${this.baseUrl}/${endpoint}`);
|
||||
}
|
||||
|
||||
private async applyRateLimit() {
|
||||
// Simplified example - real implementation would track request timing
|
||||
return new Promise(resolve => setTimeout(resolve, this.rateLimitMs));
|
||||
}
|
||||
}
|
||||
|
||||
// Factory function that configures based on user tier
|
||||
import { inject } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
const apiClientFactory = () => {
|
||||
const http = inject(HttpClient);
|
||||
const userService = inject(UserService);
|
||||
|
||||
return new ApiClient(http, userService);
|
||||
};
|
||||
|
||||
// Provider configuration
|
||||
export const apiClientProvider = {
|
||||
provide: ApiClient,
|
||||
useFactory: apiClientFactory
|
||||
};
|
||||
|
||||
// Usage in component
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
providers: [apiClientProvider]
|
||||
})
|
||||
export class DashboardComponent {
|
||||
private apiClient = inject(ApiClient);
|
||||
}
|
||||
```
|
||||
|
||||
#### useExisting
|
||||
|
||||
`useExisting` creates an alias for a provider that was already defined. Both tokens return the same instance:
|
||||
|
||||
```ts
|
||||
providers: [
|
||||
NewLogger, // The actual service
|
||||
{ provide: OldLogger, useExisting: NewLogger } // The alias
|
||||
]
|
||||
```
|
||||
|
||||
IMPORTANT: Don't confuse `useExisting` with `useClass`. `useClass` creates separate instances, while `useExisting` ensures you get the same singleton instance.
|
||||
|
||||
### Multiple providers
|
||||
|
||||
Use the `multi: true` flag when multiple providers contribute values to the same token:
|
||||
|
||||
```ts
|
||||
export const INTERCEPTOR_TOKEN = new InjectionToken<Interceptor[]>('interceptors');
|
||||
|
||||
providers: [
|
||||
{ provide: INTERCEPTOR_TOKEN, useClass: AuthInterceptor, multi: true },
|
||||
{ provide: INTERCEPTOR_TOKEN, useClass: LoggingInterceptor, multi: true },
|
||||
{ provide: INTERCEPTOR_TOKEN, useClass: RetryInterceptor, multi: true }
|
||||
]
|
||||
```
|
||||
|
||||
When you inject `INTERCEPTOR_TOKEN`, you'll receive an array containing instances of all three interceptors.
|
||||
|
||||
## Where can you specify providers?
|
||||
|
||||
Angular offers several levels where you can register providers, each with different implications for scope, lifecycle, and performance:
|
||||
|
||||
- [**Application bootstrap**](#application-bootstrap) - Global singletons available everywhere
|
||||
- [**On an element (component or directive)**](#component-or-directive-providers) - Isolated instances for specific component trees
|
||||
- [**Route**](#route-providers) - Feature-specific services for lazy-loaded modules
|
||||
|
||||
### Application bootstrap
|
||||
|
||||
Use application-level providers in `bootstrapApplication` when:
|
||||
|
||||
- **The service is used across multiple feature areas** - Services like HTTP clients, logging, or authentication that many parts of your app need
|
||||
- **You want a true singleton** - One instance shared by the entire application
|
||||
- **The service has no component-specific configuration** - General-purpose utilities that work the same everywhere
|
||||
- **You're providing global configuration** - API endpoints, feature flags, or environment settings
|
||||
|
||||
```ts
|
||||
// main.ts
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [
|
||||
{ provide: API_BASE_URL, useValue: 'https://api.example.com' },
|
||||
{ provide: INTERCEPTOR_TOKEN, useClass: AuthInterceptor, multi: true },
|
||||
LoggingService, // Used throughout the app
|
||||
{ provide: ErrorHandler, useClass: GlobalErrorHandler }
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- Single instance reduces memory usage
|
||||
- Available everywhere without additional setup
|
||||
- Easier to manage global state
|
||||
|
||||
**Drawbacks:**
|
||||
|
||||
- Always included in your JavaScript bundle, even if the value is never injected
|
||||
- Cannot be easily customized per feature
|
||||
- Harder to test individual components in isolation
|
||||
|
||||
#### Why provide during bootstrap instead of using `providedIn: 'root'`?
|
||||
|
||||
You might want a provider during bootstrap when:
|
||||
|
||||
- The provider has side-effects (e.g., installing the client-side router)
|
||||
- The provider requires configuration (e.g., routes)
|
||||
- You're using Angular's `provideSomething` pattern (e.g., `provideRouter`, `provideHttpClient`)
|
||||
|
||||
### Component or directive providers
|
||||
|
||||
Use component or directive providers when:
|
||||
|
||||
- **The service has component-specific state** - Form validators, component-specific caches, or UI state managers
|
||||
- **You need isolated instances** - Each component needs its own copy of the service
|
||||
- **The service is only used by one component tree** - Specialized services that don't need global access
|
||||
- **You're creating reusable components** - Components that should work independently with their own services
|
||||
|
||||
```angular-ts
|
||||
// Specialized form component with its own validation service
|
||||
@Component({
|
||||
selector: 'app-advanced-form',
|
||||
providers: [
|
||||
FormValidationService, // Each form gets its own validator
|
||||
{ provide: FORM_CONFIG, useValue: { strictMode: true } }
|
||||
]
|
||||
})
|
||||
export class AdvancedFormComponent { }
|
||||
|
||||
// Modal component with isolated state management
|
||||
@Component({
|
||||
selector: 'app-modal',
|
||||
providers: [
|
||||
ModalStateService // Each modal manages its own state
|
||||
]
|
||||
})
|
||||
export class ModalComponent { }
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- Better encapsulation and isolation
|
||||
- Easier to test components individually
|
||||
- Multiple instances can coexist with different configurations
|
||||
|
||||
**Drawbacks:**
|
||||
|
||||
- New instance created for each component (higher memory usage)
|
||||
- No shared state between components
|
||||
- Must be provided wherever needed
|
||||
- Always included in the same JavaScript bundle as the component or directive, even if the value is never injected
|
||||
|
||||
Note: If multiple directives on the same element provide the same token, one will win, but which one is undefined.
|
||||
|
||||
### Route providers
|
||||
|
||||
Use route-level providers for:
|
||||
|
||||
- **Feature-specific services** - Services only needed for particular routes or feature modules
|
||||
- **Lazy-loaded module dependencies** - Services that should only load with specific features
|
||||
- **Route-specific configuration** - Settings that vary by application area
|
||||
|
||||
```ts
|
||||
// routes.ts
|
||||
export const routes: Routes = [
|
||||
{
|
||||
path: 'admin',
|
||||
providers: [
|
||||
AdminService, // Only loaded with admin routes
|
||||
{ provide: FEATURE_FLAGS, useValue: { adminMode: true } }
|
||||
],
|
||||
loadChildren: () => import('./admin/admin.routes')
|
||||
},
|
||||
{
|
||||
path: 'shop',
|
||||
providers: [
|
||||
ShoppingCartService, // Isolated shopping state
|
||||
PaymentService
|
||||
],
|
||||
loadChildren: () => import('./shop/shop.routes')
|
||||
}
|
||||
];
|
||||
```
|
||||
|
||||
## Library author patterns
|
||||
|
||||
When creating Angular libraries, you often need to provide flexible configuration options for consumers while maintaining clean APIs. Angular's own libraries demonstrate powerful patterns for achieving this.
|
||||
|
||||
### The `provide` pattern
|
||||
|
||||
Instead of requiring users to manually configure complex providers, library authors can export functions that return provider configurations:
|
||||
|
||||
```ts
|
||||
// 📁 /libs/analytics/src/providers.ts
|
||||
import { InjectionToken, Provider, inject } from '@angular/core';
|
||||
|
||||
// Configuration interface
|
||||
export interface AnalyticsConfig {
|
||||
trackingId: string;
|
||||
enableDebugMode?: boolean;
|
||||
anonymizeIp?: boolean;
|
||||
}
|
||||
|
||||
// Internal token for configuration
|
||||
const ANALYTICS_CONFIG = new InjectionToken<AnalyticsConfig>('analytics.config');
|
||||
|
||||
// Main service that uses the configuration
|
||||
export class AnalyticsService {
|
||||
private config = inject(ANALYTICS_CONFIG);
|
||||
|
||||
track(event: string, properties?: any) {
|
||||
// Implementation using config
|
||||
}
|
||||
}
|
||||
|
||||
// Provider function for consumers
|
||||
export function provideAnalytics(config: AnalyticsConfig): Provider[] {
|
||||
return [
|
||||
{ provide: ANALYTICS_CONFIG, useValue: config },
|
||||
AnalyticsService
|
||||
];
|
||||
}
|
||||
|
||||
// Usage in consumer app
|
||||
// main.ts
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [
|
||||
provideAnalytics({
|
||||
trackingId: 'GA-12345',
|
||||
enableDebugMode: !environment.production
|
||||
})
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### Advanced provider patterns with options
|
||||
|
||||
For more complex scenarios, you can combine multiple configuration approaches:
|
||||
|
||||
```ts
|
||||
// 📁 /libs/http-client/src/provider.ts
|
||||
import { Provider, InjectionToken, inject } from '@angular/core';
|
||||
|
||||
// Feature flags for optional functionality
|
||||
export enum HttpFeatures {
|
||||
Interceptors = 'interceptors',
|
||||
Caching = 'caching',
|
||||
Retry = 'retry'
|
||||
}
|
||||
|
||||
// Configuration interfaces
|
||||
export interface HttpConfig {
|
||||
baseUrl?: string;
|
||||
timeout?: number;
|
||||
headers?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface RetryConfig {
|
||||
maxAttempts: number;
|
||||
delayMs: number;
|
||||
}
|
||||
|
||||
// Internal tokens
|
||||
const HTTP_CONFIG = new InjectionToken<HttpConfig>('http.config');
|
||||
const RETRY_CONFIG = new InjectionToken<RetryConfig>('retry.config');
|
||||
const HTTP_FEATURES = new InjectionToken<Set<HttpFeatures>>('http.features');
|
||||
|
||||
// Core service
|
||||
class HttpClientService {
|
||||
private config = inject(HTTP_CONFIG, { optional: true });
|
||||
private features = inject(HTTP_FEATURES);
|
||||
|
||||
get(url: string) {
|
||||
// Use config and check features
|
||||
}
|
||||
}
|
||||
|
||||
// Feature services
|
||||
class RetryInterceptor {
|
||||
private config = inject(RETRY_CONFIG);
|
||||
// Retry logic
|
||||
}
|
||||
|
||||
class CacheInterceptor {
|
||||
// Caching logic
|
||||
}
|
||||
|
||||
// Main provider function
|
||||
export function provideHttpClient(
|
||||
config?: HttpConfig,
|
||||
...features: HttpFeature[]
|
||||
): Provider[] {
|
||||
const providers: Provider[] = [
|
||||
{ provide: HTTP_CONFIG, useValue: config || {} },
|
||||
{ provide: HTTP_FEATURES, useValue: new Set(features.map(f => f.kind)) },
|
||||
HttpClientService
|
||||
];
|
||||
|
||||
// Add feature-specific providers
|
||||
features.forEach(feature => {
|
||||
providers.push(...feature.providers);
|
||||
});
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
||||
// Feature configuration functions
|
||||
export interface HttpFeature {
|
||||
kind: HttpFeatures;
|
||||
providers: Provider[];
|
||||
}
|
||||
|
||||
export function withInterceptors(...interceptors: any[]): HttpFeature {
|
||||
return {
|
||||
kind: HttpFeatures.Interceptors,
|
||||
providers: interceptors.map(interceptor => ({
|
||||
provide: INTERCEPTOR_TOKEN,
|
||||
useClass: interceptor,
|
||||
multi: true
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
export function withCaching(): HttpFeature {
|
||||
return {
|
||||
kind: HttpFeatures.Caching,
|
||||
providers: [CacheInterceptor]
|
||||
};
|
||||
}
|
||||
|
||||
export function withRetry(config: RetryConfig): HttpFeature {
|
||||
return {
|
||||
kind: HttpFeatures.Retry,
|
||||
providers: [
|
||||
{ provide: RETRY_CONFIG, useValue: config },
|
||||
RetryInterceptor
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
// Consumer usage with multiple features
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [
|
||||
provideHttpClient(
|
||||
{ baseUrl: 'https://api.example.com' },
|
||||
withInterceptors(AuthInterceptor, LoggingInterceptor),
|
||||
withCaching(),
|
||||
withRetry({ maxAttempts: 3, delayMs: 1000 })
|
||||
)
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### Why use provider functions instead of direct configuration?
|
||||
|
||||
Provider functions offer several advantages for library authors:
|
||||
|
||||
1. **Encapsulation** - Internal tokens and implementation details remain private
|
||||
2. **Type safety** - TypeScript ensures correct configuration at compile time
|
||||
3. **Flexibility** - Easily compose features with `with*` pattern
|
||||
4. **Future-proofing** - Internal implementation can change without breaking consumers
|
||||
5. **Consistency** - Aligns with Angular's own patterns (`provideRouter`, `provideHttpClient`, etc.)
|
||||
|
||||
This pattern is extensively used in Angular's own libraries and is considered a best practice for library authors who need to provide configurable services.
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
# Injection context
|
||||
|
||||
The dependency injection (DI) system relies internally on a runtime context where the current injector is available.
|
||||
|
||||
This means that injectors can only work when code is executed in such a context.
|
||||
|
||||
The injection context is available in these situations:
|
||||
|
|
@ -13,20 +14,7 @@ The injection context is available in these situations:
|
|||
|
||||
Knowing when you are in an injection context will allow you to use the [`inject`](api/core/inject) function to inject instances.
|
||||
|
||||
## Class constructors
|
||||
|
||||
Every time the DI system instantiates a class, it does so in an injection context. This is handled by the framework itself. The constructor of the class is executed in that runtime context, which also allows injection of a token using the [`inject`](api/core/inject) function.
|
||||
|
||||
<docs-code language="typescript" highlight="[[3],[6]]">
|
||||
class MyComponent {
|
||||
private service1: Service1;
|
||||
private service2: Service2 = inject(Service2); // In context
|
||||
|
||||
constructor() {
|
||||
this.service1 = inject(Service1) // In context
|
||||
}
|
||||
}
|
||||
</docs-code>
|
||||
NOTE: For basic examples of using `inject()` in class constructors and field initializers, see the [overview guide](guide/di/overview#where-can-inject-be-used).
|
||||
|
||||
## Stack frame in context
|
||||
|
||||
|
|
@ -49,16 +37,16 @@ This requires access to a given injector, like the `EnvironmentInjector`, for ex
|
|||
<docs-code header="src/app/heroes/hero.service.ts" language="typescript"
|
||||
highlight="[9]">
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class HeroService {
|
||||
private environmentInjector = inject(EnvironmentInjector);
|
||||
private environmentInjector = inject(EnvironmentInjector);
|
||||
|
||||
someMethod() {
|
||||
runInInjectionContext(this.environmentInjector, () => {
|
||||
inject(SomeService); // Do what you need with the injected service
|
||||
});
|
||||
}
|
||||
someMethod() {
|
||||
runInInjectionContext(this.environmentInjector, () => {
|
||||
inject(SomeService); // Do what you need with the injected service
|
||||
});
|
||||
}
|
||||
}
|
||||
</docs-code>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,212 +0,0 @@
|
|||
# Configuring dependency providers
|
||||
|
||||
The previous sections described how to use class instances as dependencies.
|
||||
Aside from classes, you can also use values such as `boolean`, `string`, `Date`, and objects as dependencies.
|
||||
Angular provides the necessary APIs to make the dependency configuration flexible, so you can make those values available in DI.
|
||||
|
||||
## Specifying a provider token
|
||||
|
||||
If you specify the service class as the provider token, the default behavior is for the injector to instantiate that class using the `new` operator.
|
||||
|
||||
In the following example, the app component provides a `Logger` instance:
|
||||
|
||||
<docs-code header="src/app/app.component.ts" language="typescript">
|
||||
providers: [Logger],
|
||||
</docs-code>
|
||||
|
||||
You can, however, configure DI to associate the `Logger` provider token with a different class or any other value.
|
||||
So when the `Logger` is injected, the configured value is used instead.
|
||||
|
||||
In fact, the class provider syntax is a shorthand expression that expands into a provider configuration, defined by the `Provider` interface.
|
||||
Angular expands the `providers` value in this case into a full provider object as follows:
|
||||
|
||||
<docs-code header="src/app/app.component.ts" language="typescript">
|
||||
[{ provide: Logger, useClass: Logger }]
|
||||
</docs-code>
|
||||
|
||||
The expanded provider configuration is an object literal with two properties:
|
||||
|
||||
- The `provide` property holds the token that serves as the key for consuming the dependency value.
|
||||
- The second property is a provider definition object, which tells the injector **how** to create the dependency value. The provider-definition can be one of the following:
|
||||
- `useClass` - this option tells Angular DI to instantiate a provided class when a dependency is injected
|
||||
- `useExisting` - allows you to alias a token and reference any existing one.
|
||||
- `useFactory` - allows you to define a function that constructs a dependency.
|
||||
- `useValue` - provides a static value that should be used as a dependency.
|
||||
|
||||
The sections below describe how to use the different provider definitions.
|
||||
|
||||
### Class providers: useClass
|
||||
|
||||
The `useClass` provider key lets you create and return a new instance of the specified class.
|
||||
|
||||
You can use this type of provider to substitute an alternative implementation for a common or default class.
|
||||
The alternative implementation can, for example, implement a different strategy, extend the default class, or emulate the behavior of the real class in a test case.
|
||||
|
||||
In the following example, `BetterLogger` would be instantiated when the `Logger` dependency is requested in a component or any other class:
|
||||
|
||||
<docs-code header="src/app/app.component.ts" language="typescript">
|
||||
[{ provide: Logger, useClass: BetterLogger }]
|
||||
</docs-code>
|
||||
|
||||
If the alternative class providers have their own dependencies, specify both providers in the providers metadata property of the parent module or component:
|
||||
|
||||
<docs-code header="src/app/app.component.ts" language="typescript">
|
||||
[
|
||||
UserService, // dependency needed in `EvenBetterLogger`.
|
||||
{ provide: Logger, useClass: EvenBetterLogger },
|
||||
]
|
||||
</docs-code>
|
||||
|
||||
In this example, `EvenBetterLogger` displays the user name in the log message. This logger gets the user from an injected `UserService` instance:
|
||||
|
||||
<docs-code header="src/app/even-better-logger.component.ts" language="typescript"
|
||||
highlight="[[3],[6]]">
|
||||
@Injectable()
|
||||
export class EvenBetterLogger extends Logger {
|
||||
private userService = inject(UserService);
|
||||
|
||||
override log(message: string) {
|
||||
const name = this.userService.user.name;
|
||||
super.log(`Message to ${name}: ${message}`);
|
||||
}
|
||||
}
|
||||
</docs-code>
|
||||
|
||||
Angular DI knows how to construct the `UserService` dependency, since it has been configured above and is available in the injector.
|
||||
|
||||
### Alias providers: useExisting
|
||||
|
||||
The `useExisting` provider key lets you map one token to another.
|
||||
In effect, the first token is an alias for the service associated with the second token, creating two ways to access the same service object.
|
||||
|
||||
In the following example, the injector injects the singleton instance of `NewLogger` when the component asks for either the new or the old logger:
|
||||
In this way, `OldLogger` is an alias for `NewLogger`.
|
||||
|
||||
<docs-code header="src/app/app.component.ts" language="typescript" highlight="[4]">
|
||||
[
|
||||
NewLogger,
|
||||
// Alias OldLogger w/ reference to NewLogger
|
||||
{ provide: OldLogger, useExisting: NewLogger},
|
||||
]
|
||||
</docs-code>
|
||||
|
||||
NOTE: Ensure you do not alias `OldLogger` to `NewLogger` with `useClass`, as this creates two different `NewLogger` instances.
|
||||
|
||||
### Factory providers: useFactory
|
||||
|
||||
The `useFactory` provider key lets you create a dependency object by calling a factory function.
|
||||
With this approach, you can create a dynamic value based on information available in the DI and elsewhere in the app.
|
||||
|
||||
In the following example, only authorized users should see secret heroes in the `HeroService`.
|
||||
Authorization can change during the course of a single application session, as when a different user logs in .
|
||||
|
||||
To keep security-sensitive information in `UserService` and out of `HeroService`, give the `HeroService` constructor a boolean flag to control display of secret heroes:
|
||||
|
||||
<docs-code header="src/app/heroes/hero.service.ts" language="typescript"
|
||||
highlight="[[4],[7]]">
|
||||
class HeroService {
|
||||
constructor(
|
||||
private logger: Logger,
|
||||
private isAuthorized: boolean) { }
|
||||
|
||||
getHeroes() {
|
||||
const auth = this.isAuthorized ? 'authorized' : 'unauthorized';
|
||||
this.logger.log(`Getting heroes for ${auth} user.`);
|
||||
return HEROES.filter(hero => this.isAuthorized || !hero.isSecret);
|
||||
}
|
||||
}
|
||||
</docs-code>
|
||||
|
||||
To implement the `isAuthorized` flag, use a factory provider to create a new logger instance for `HeroService`.
|
||||
This is necessary as we need to manually pass `Logger` when constructing the hero service:
|
||||
|
||||
<docs-code header="src/app/heroes/hero.service.provider.ts" language="typescript">
|
||||
const heroServiceFactory = (logger: Logger, userService: UserService) =>
|
||||
new HeroService(logger, userService.user.isAuthorized);
|
||||
</docs-code>
|
||||
|
||||
The factory function has access to `UserService`.
|
||||
You inject both `Logger` and `UserService` into the factory provider so the injector can pass them along to the factory function:
|
||||
|
||||
<docs-code header="src/app/heroes/hero.service.provider.ts" language="typescript"
|
||||
highlight="[3,4]">
|
||||
export const heroServiceProvider = {
|
||||
provide: HeroService,
|
||||
useFactory: heroServiceFactory,
|
||||
deps: [Logger, UserService]
|
||||
};
|
||||
</docs-code>
|
||||
|
||||
- The `useFactory` field specifies that the provider is a factory function whose implementation is `heroServiceFactory`.
|
||||
- The `deps` property is an array of provider tokens.
|
||||
The `Logger` and `UserService` classes serve as tokens for their own class providers.
|
||||
The injector resolves these tokens and injects the corresponding services into the matching `heroServiceFactory` factory function parameters, based on the order specified.
|
||||
|
||||
Capturing the factory provider in the exported variable, `heroServiceProvider`, makes the factory provider reusable.
|
||||
|
||||
### Value providers: useValue
|
||||
|
||||
The `useValue` key lets you associate a static value with a DI token.
|
||||
|
||||
Use this technique to provide runtime configuration constants such as website base addresses and feature flags.
|
||||
You can also use a value provider in a unit test to provide mock data in place of a production data service.
|
||||
|
||||
The next section provides more information about the `useValue` key.
|
||||
|
||||
## Using an `InjectionToken` object
|
||||
|
||||
Use an `InjectionToken` object as provider token for non-class dependencies.
|
||||
The following example defines a token, `APP_CONFIG`. of the type `InjectionToken`:
|
||||
|
||||
<docs-code header="src/app/app.config.ts" language="typescript" highlight="[3]">
|
||||
import { InjectionToken } from '@angular/core';
|
||||
|
||||
export interface AppConfig {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config description');
|
||||
</docs-code>
|
||||
|
||||
The optional type parameter, `<AppConfig>`, and the token description, `app.config description`, specify the token's purpose.
|
||||
|
||||
Next, register the dependency provider in the component using the `InjectionToken` object of `APP_CONFIG`:
|
||||
|
||||
<docs-code header="src/app/app.component.ts" language="typescript">
|
||||
const MY_APP_CONFIG_VARIABLE: AppConfig = {
|
||||
title: 'Hello',
|
||||
};
|
||||
|
||||
providers: [{ provide: APP_CONFIG, useValue: MY_APP_CONFIG_VARIABLE }]
|
||||
</docs-code>
|
||||
|
||||
Now, inject the configuration object in the constructor body with the `inject` function:
|
||||
|
||||
<docs-code header="src/app/app.component.ts" language="typescript" highlight="[2]">
|
||||
export class AppComponent {
|
||||
constructor() {
|
||||
const config = inject(APP_CONFIG);
|
||||
this.title = config.title;
|
||||
}
|
||||
}
|
||||
</docs-code>
|
||||
|
||||
### Interfaces and DI
|
||||
|
||||
Though the TypeScript `AppConfig` interface supports typing within the class, the `AppConfig` interface plays no role in DI.
|
||||
In TypeScript, an interface is a design-time artifact, and does not have a runtime representation, or token, that the DI framework can use.
|
||||
|
||||
When the TypeScript transpiles to JavaScript, the interface disappears because JavaScript doesn't have interfaces.
|
||||
Because there is no interface for Angular to find at runtime, the interface cannot be a token, nor can you inject it:
|
||||
|
||||
<docs-code header="src/app/app.component.ts" language="typescript">
|
||||
// Can't use interface as provider token
|
||||
[{ provide: AppConfig, useValue: MY_APP_CONFIG_VARIABLE })]
|
||||
</docs-code>
|
||||
|
||||
<docs-code header="src/app/app.component.ts" language="typescript" highlight="[3]">
|
||||
export class AppComponent {
|
||||
// Can't inject using the interface as the parameter type
|
||||
private config = inject(AppConfig);
|
||||
}
|
||||
</docs-code>
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
# Understanding dependency injection
|
||||
|
||||
Dependency injection, or DI, is one of the fundamental concepts in Angular. DI is wired into the Angular framework and allows classes with Angular decorators, such as Components, Directives, Pipes, and Injectables, to configure dependencies that they need.
|
||||
|
||||
Two main roles exist in the DI system: dependency consumer and dependency provider.
|
||||
|
||||
Angular facilitates the interaction between dependency consumers and dependency providers using an abstraction called `Injector`. When a dependency is requested, the injector checks its registry to see if there is an instance already available there. If not, a new instance is created and stored in the registry. Angular creates an application-wide injector (also known as the "root" injector) during the application bootstrap process. In most cases you don't need to manually create injectors, but you should know that there is a layer that connects providers and consumers.
|
||||
|
||||
This topic covers basic scenarios of how a class can act as a dependency. Angular also allows you to use functions, objects, primitive types such as string or Boolean, or any other types as dependencies. For more information, see [Dependency providers](guide/di/dependency-injection-providers).
|
||||
|
||||
## Providing a dependency
|
||||
|
||||
Consider a class called `HeroService` that needs to act as a dependency in a component.
|
||||
|
||||
The first step is to add the `@Injectable` decorator to show that the class can be injected.
|
||||
|
||||
<docs-code language="typescript" highlight="[1]">
|
||||
@Injectable()
|
||||
class HeroService {}
|
||||
</docs-code>
|
||||
|
||||
The next step is to make it available in the DI by providing it.
|
||||
A dependency can be provided in multiple places:
|
||||
|
||||
- [**Preferred**: At the application root level using `providedIn`](#preferred-at-the-application-root-level-using-providedin)
|
||||
- [At the Component level](#at-the-component-level)
|
||||
- [At the application root level using `ApplicationConfig`](#at-the-application-root-level-using-applicationconfig)
|
||||
- [`NgModule` based applications](#ngmodule-based-applications)
|
||||
|
||||
### **Preferred**: At the application root level using `providedIn`
|
||||
|
||||
Providing a service at the application root level using `providedIn` allows injecting the service into all other classes.
|
||||
Using `providedIn` enables Angular and JavaScript code optimizers to effectively remove services that are unused (known as tree-shaking).
|
||||
|
||||
You can provide a service by using `providedIn: 'root'` in the `@Injectable` decorator:
|
||||
|
||||
<docs-code language="typescript" highlight="[2]">
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
class HeroService {}
|
||||
</docs-code>
|
||||
|
||||
When you provide the service at the root level, Angular creates a single, shared instance of the `HeroService` and injects it into any class that asks for it.
|
||||
|
||||
### At the Component level
|
||||
|
||||
You can provide services at `@Component` level by using the `providers` field of the `@Component` decorator.
|
||||
In this case the `HeroService` becomes available to all instances of this component and other components and directives used in the template.
|
||||
|
||||
For example:
|
||||
|
||||
<docs-code language="typescript" highlight="[4]">
|
||||
@Component({
|
||||
selector: 'hero-list',
|
||||
template: '...',
|
||||
providers: [HeroService]
|
||||
})
|
||||
class HeroListComponent {}
|
||||
</docs-code>
|
||||
|
||||
When you register a provider at the component level, you get a new instance of the service with each new instance of that component.
|
||||
|
||||
NOTE: Declaring a service like this causes `HeroService` to always be included in your application— even if the service is unused.
|
||||
|
||||
### At the application root level using `ApplicationConfig`
|
||||
|
||||
You can use the `providers` field of the `ApplicationConfig` (passed to the `bootstrapApplication` function) to provide a service or other `Injectable` at the application level.
|
||||
|
||||
In the example below, the `HeroService` is available to all components, directives, and pipes:
|
||||
|
||||
<docs-code language="typescript" highlight="[3]">
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
{ provide: HeroService },
|
||||
]
|
||||
};
|
||||
</docs-code>
|
||||
|
||||
Then, in `main.ts`:
|
||||
|
||||
<docs-code language="typescript">
|
||||
bootstrapApplication(AppComponent, appConfig)
|
||||
</docs-code>
|
||||
|
||||
NOTE: Declaring a service like this causes `HeroService` to always be included in your application— even if the service is unused.
|
||||
|
||||
### `NgModule` based applications
|
||||
|
||||
`@NgModule`-based applications use the `providers` field of the `@NgModule` decorator to provide a service or other `Injectable` available at the application level.
|
||||
|
||||
A service provided in a module is available to all declarations of the module, or to any other modules which share the same `ModuleInjector`.
|
||||
To understand all edge-cases, see [Hierarchical injectors](guide/di/hierarchical-dependency-injection).
|
||||
|
||||
NOTE: Declaring a service using `providers` causes the service to be included in your application— even if the service is unused.
|
||||
|
||||
## Injecting/consuming a dependency
|
||||
|
||||
Use Angular's `inject` function to retrieve dependencies.
|
||||
|
||||
```ts
|
||||
import {inject, Component} from 'angular/core';
|
||||
|
||||
@Component({/* ... */})
|
||||
export class UserProfile {
|
||||
// You can use the `inject` function in property initializers.
|
||||
private userClient = inject(UserClient);
|
||||
|
||||
constructor() {
|
||||
// You can also use the `inject` function in a constructor.
|
||||
const logger = inject(Logger);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can use the `inject` function in any [injection context](guide/di/dependency-injection-context). Most of the time, this is in a class property initializer or a class constructor for components, directives, services, and pipes.
|
||||
|
||||
When Angular discovers that a component depends on a service, it first checks if the injector has any existing instances of that service. If a requested service instance doesn't yet exist, the injector creates one using the registered provider, and adds it to the injector before returning the service to Angular.
|
||||
|
||||
When all requested services have been resolved and returned, Angular can call the component's constructor with those services as arguments.
|
||||
|
||||
```mermaid
|
||||
graph TD;
|
||||
subgraph Injector
|
||||
serviceA[Service A]
|
||||
heroService[HeroService]
|
||||
serviceC[Service C]
|
||||
serviceD[Service D]
|
||||
end
|
||||
direction TB
|
||||
componentProperty["Component <br> heroService = inject(HeroService)"]
|
||||
heroService-->componentProperty
|
||||
style componentProperty text-align: left
|
||||
```
|
||||
|
||||
## What's next
|
||||
|
||||
<docs-pill-row>
|
||||
<docs-pill href="/guide/di/creating-injectable-service" title="Creating an injectable service"/>
|
||||
</docs-pill-row>
|
||||
|
|
@ -2,40 +2,7 @@
|
|||
|
||||
This guide explores additional features of dependency injection in Angular.
|
||||
|
||||
## Custom providers with `@Inject`
|
||||
|
||||
Using a custom provider allows you to provide a concrete implementation for implicit dependencies, such as built-in browser APIs.
|
||||
The following example uses an `InjectionToken` to provide the [localStorage](https://developer.mozilla.org/docs/Web/API/Window/localStorage) browser API as a dependency in the `BrowserStorageService`:
|
||||
|
||||
<docs-code header="src/app/storage.service.ts" language="typescript"
|
||||
highlight="[[3,6],[12]]">
|
||||
import { inject, Injectable, InjectionToken } from '@angular/core';
|
||||
|
||||
export const BROWSER_STORAGE = new InjectionToken<Storage>('Browser Storage', {
|
||||
providedIn: 'root',
|
||||
factory: () => localStorage
|
||||
});
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class BrowserStorageService {
|
||||
public storage = inject(BROWSER_STORAGE);
|
||||
|
||||
get(key: string) {
|
||||
return this.storage.getItem(key);
|
||||
}
|
||||
|
||||
set(key: string, value: string) {
|
||||
this.storage.setItem(key, value);
|
||||
}
|
||||
}
|
||||
</docs-code>
|
||||
|
||||
The `factory` function returns the `localStorage` property that is attached to the browser's window object.
|
||||
The `inject` function initializes the `storage` property with an instance of the token.
|
||||
|
||||
This custom provider can now be overridden during testing with a mock API of `localStorage` instead of interacting with real browser APIs.
|
||||
NOTE: For comprehensive coverage of InjectionToken and custom providers, see the [defining dependency providers guide](guide/di/defining-dependency-providers#injection-tokens).
|
||||
|
||||
## Inject the component's DOM element
|
||||
|
||||
|
|
@ -48,14 +15,14 @@ Angular exposes the underlying element of a `@Component` or `@Directive` via inj
|
|||
import { Directive, ElementRef } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[appHighlight]'
|
||||
selector: '[appHighlight]'
|
||||
})
|
||||
export class HighlightDirective {
|
||||
private element = inject(ElementRef)
|
||||
private element = inject(ElementRef)
|
||||
|
||||
update() {
|
||||
this.element.nativeElement.style.color = 'red';
|
||||
}
|
||||
update() {
|
||||
this.element.nativeElement.style.color = 'red';
|
||||
}
|
||||
}
|
||||
</docs-code>
|
||||
|
||||
|
|
@ -64,13 +31,13 @@ export class HighlightDirective {
|
|||
The order of class declaration matters in TypeScript.
|
||||
You can't refer directly to a class until it's been defined.
|
||||
|
||||
This isn't usually a problem, especially if you adhere to the recommended *one class per file* rule.
|
||||
This isn't usually a problem, especially if you adhere to the recommended _one class per file_ rule.
|
||||
But sometimes circular references are unavoidable.
|
||||
For example, when class 'A' refers to class 'B' and 'B' refers to 'A', one of them has to be defined first.
|
||||
|
||||
The Angular `forwardRef()` function creates an *indirect* reference that Angular can resolve later.
|
||||
The Angular `forwardRef()` function creates an _indirect_ reference that Angular can resolve later.
|
||||
|
||||
You face a similar problem when a class makes *a reference to itself*.
|
||||
You face a similar problem when a class makes _a reference to itself_.
|
||||
For example, in its `providers` array.
|
||||
The `providers` array is a property of the `@Component()` decorator function, which must appear before the class definition.
|
||||
You can break such circular references by using `forwardRef`.
|
||||
|
|
|
|||
|
|
@ -1,14 +1,8 @@
|
|||
# Hierarchical injectors
|
||||
|
||||
Injectors in Angular have rules that you can leverage to achieve the desired visibility of injectables in your applications.
|
||||
By understanding these rules, you can determine whether to declare a provider at the application level, in a Component, or in a Directive.
|
||||
This guide provides in-depth coverage of Angular's hierarchical dependency injection system, including resolution rules, modifiers, and advanced patterns.
|
||||
|
||||
The applications you build with Angular can become quite large, and one way to manage this complexity is to split up the application into a well-defined tree of components.
|
||||
|
||||
There can be sections of your page that work in a completely independent way than the rest of the application, with its own local copies of the services and other dependencies that it needs.
|
||||
Some of the services that these sections of the application use might be shared with other parts of the application, or with parent components that are further up in the component tree, while other dependencies are meant to be private.
|
||||
|
||||
With hierarchical dependency injection, you can isolate sections of the application and give them their own private dependencies not shared with the rest of the application, or have parent components share certain dependencies with its child components only but not with the rest of the component tree, and so on. Hierarchical dependency injection enables you to share dependencies between different parts of the application only when and if you need to.
|
||||
NOTE: For basic concepts about injector hierarchy and provider scoping, see the [defining dependency providers guide](guide/di/defining-dependency-providers#injector-hierarchy-in-angular).
|
||||
|
||||
## Types of injector hierarchies
|
||||
|
||||
|
|
|
|||
|
|
@ -1,29 +1,154 @@
|
|||
<docs-decorative-header title="Dependency injection in Angular" imgSrc="adev/src/assets/images/dependency_injection.svg"> <!-- markdownlint-disable-line -->
|
||||
"DI" is a design pattern and mechanism for creating and delivering some parts of an app to other parts of an app that require them.
|
||||
|
||||
Dependency Injection (DI) is a design pattern used to organize and share code across an application.
|
||||
</docs-decorative-header>
|
||||
|
||||
TIP: Check out Angular's [Essentials](essentials/dependency-injection) before diving into this comprehensive guide.
|
||||
|
||||
When you develop a smaller part of your system, like a module or a class, you may need to use features from other classes. For example, you may need an HTTP service to make backend calls. Dependency Injection, or DI, is a design pattern and mechanism for creating and delivering some parts of an application to other parts of an application that require them. Angular supports this design pattern and you can use it in your applications to increase flexibility and modularity.
|
||||
As an application grows, developers often need to reuse and share features across different parts of the codebase. [Dependency Injection (DI)](https://en.wikipedia.org/wiki/Dependency_injection) is a design pattern used to organize and share code across an application by allowing you to "inject" features into different parts.
|
||||
|
||||
In Angular, dependencies are typically services, but they also can be values, such as strings or functions. An injector for an application (created automatically during bootstrap) instantiates dependencies when needed, using a configured provider of the service or value.
|
||||
Dependency injection is a popular pattern because it allows developers to address common challenges such as:
|
||||
|
||||
## Learn about Angular dependency injection
|
||||
- **Improved code maintainability**: Dependency injection allows cleaner separation of concerns which enables easier refactoring and reducing code duplication.
|
||||
- **Scalability**: Modular functionality can be reused across multiple contexts and allows for easier scaling.
|
||||
- **Better testing**: DI allows unit tests to easily use [test doubles](https://en.wikipedia.org/wiki/Test_double) for situations when using a real implementation is not practical.
|
||||
|
||||
<docs-card-container>
|
||||
<docs-card title="Understanding dependency injection" href="/guide/di/dependency-injection">
|
||||
Learn basic principles of dependency injection in Angular.
|
||||
</docs-card>
|
||||
<docs-card title="Creating and injecting service" href="/guide/di/creating-injectable-service">
|
||||
Describes how to create a service and inject it in other services and components.
|
||||
</docs-card>
|
||||
<docs-card title="Configuring dependency providers" href="/guide/di/dependency-injection-providers">
|
||||
Describes how to configure dependencies using the providers field on the @Component and @NgModule decorators. Also describes how to use InjectionToken to provide and inject values in DI, which can be helpful when you want to use a value other than classes as dependencies.
|
||||
</docs-card>
|
||||
<docs-card title="Injection context" href="/guide/di/dependency-injection-context">
|
||||
Describes what an injection context is and how to use the DI system where you need it.
|
||||
</docs-card>
|
||||
<docs-card title="Hierarchical injectors" href="/guide/di/hierarchical-dependency-injection">
|
||||
Hierarchical DI enables you to share dependencies between different parts of the application only when and if you need to. This is an advanced topic.
|
||||
</docs-card>
|
||||
</docs-card-container>
|
||||
## How does dependency injection work in Angular?
|
||||
|
||||
A dependency is any object, value, function or service that a class needs to work but does not create itself. In other words, it creates a relationship between different parts of your application since it wouldn't work without the dependency.
|
||||
|
||||
There are two ways that code interacts with any dependency injection system:
|
||||
|
||||
- Code can _provide_, or make available, values.
|
||||
- Code can _inject_, or ask for, those values as dependencies.
|
||||
|
||||
"Values," in this context, can be any JavaScript value, including objects and functions. Common types of injected dependencies include:
|
||||
|
||||
- **Configuration values**: Environment-specific constants, API URLs, feature flags, etc.
|
||||
- **Factories**: Functions that create objects or values based on runtime conditions
|
||||
- **Services**: Classes that provide common functionality, business logic, or state
|
||||
|
||||
Angular components and directives automatically participate in DI, meaning that they can inject dependencies _and_ they are available to be injected.
|
||||
|
||||
## What are services?
|
||||
|
||||
An Angular _service_ is a TypeScript class decorated with `@Injectable`, which makes an instance of the class available to be injected as a dependency. Services are the most common way of sharing data and functionality across an application.
|
||||
|
||||
Common types of services include:
|
||||
|
||||
- **Data clients:** Abstracts the details of making requests to a server for data retrieval and mutation
|
||||
- **State management:** Defines state shared across multiple components or pages
|
||||
- **Authentication and authorization:** Manages user authentication, token storage, and access control
|
||||
- **Logging and error handling:** Establishes a common API for logging or communicating error states to the user
|
||||
- **Event handling and dispatch:** Handles events or notifications that are not associated with a specific component, or for dispatching events and notifications to components, following the [observer pattern](https://en.wikipedia.org/wiki/Observer_pattern)
|
||||
- **Utility functions:** Offers reusable utility functions like data formatting, validation, or calculations
|
||||
|
||||
The following example declares a service named `AnalyticsLogger`:
|
||||
|
||||
```ts
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AnalyticsLogger {
|
||||
trackEvent(category: string, value: string) {
|
||||
console.log('Analytics event logged:', {
|
||||
category,
|
||||
value,
|
||||
timestamp: new Date().toISOString()
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
NOTE: The `providedIn: 'root'` option makes this service available throughout your entire application as a singleton. This is the recommended approach for most services.
|
||||
|
||||
## Injecting dependencies with `inject()`
|
||||
|
||||
You can inject dependencies using Angular's `inject()` function.
|
||||
|
||||
Here is an example of a navigation bar that injects `AnalyticsLogger` and Angular `Router` service to allow users to navigate to a different page while tracking the event.
|
||||
|
||||
```angular-ts
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { AnalyticsLogger } from './analytics-logger';
|
||||
|
||||
@Component({
|
||||
selector: 'app-navbar',
|
||||
template: `
|
||||
<a href="#" (click)="navigateToDetail($event)">Detail Page</a>
|
||||
`,
|
||||
})
|
||||
export class NavbarComponent {
|
||||
private router = inject(Router);
|
||||
private analytics = inject(AnalyticsLogger);
|
||||
|
||||
navigateToDetail(event: Event) {
|
||||
event.preventDefault();
|
||||
this.analytics.trackEvent('navigation', '/details');
|
||||
this.router.navigate(['/details']);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Where can `inject()` be used?
|
||||
|
||||
You can inject dependencies during construction of a component, directive, or service. The call to `inject` can appear in either the `constructor` or in a field initializer. Here are some common examples:
|
||||
|
||||
```ts
|
||||
@Component({...})
|
||||
export class MyComponent {
|
||||
// ✅ In class field initializer
|
||||
private service = inject(MyService);
|
||||
|
||||
// ✅ In constructor body
|
||||
private anotherService: MyService;
|
||||
|
||||
constructor() {
|
||||
this.anotherService = inject(MyService);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
@Directive({...})
|
||||
export class MyDirective {
|
||||
// ✅ In class field initializer
|
||||
private element = inject(ElementRef);
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class MyService {
|
||||
// ✅ In a service
|
||||
private http = inject(HttpClient);
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
export const authGuard = () => {
|
||||
// ✅ In a route guard
|
||||
const auth = inject(AuthService);
|
||||
return auth.isAuthenticated();
|
||||
}
|
||||
```
|
||||
|
||||
Angular uses the term "injection context" to describe any place in your code where you can call `inject`. While component, directive, and service construction is the most common, see [injection contexts](/guide/di/dependency-injection-context) for more details.
|
||||
|
||||
For more information, see the [inject API docs](api/core/inject#usage-notes).
|
||||
|
||||
## Next steps
|
||||
|
||||
Now that you understand the fundamentals of dependency injection in Angular, you're ready to learn how to create your own services.
|
||||
|
||||
The next guide, [Creating and using services](guide/di/creating-and-using-services), will show you:
|
||||
|
||||
- How to create a service with the Angular CLI or manually
|
||||
- How the `providedIn: 'root'` pattern works
|
||||
- How to inject services into components and other services
|
||||
|
||||
This covers the most common use case for services in Angular applications.
|
||||
|
|
|
|||
Loading…
Reference in a new issue