Skip to main content
Dependency Injection (DI) is a design pattern and core feature of Angular that allows you to declare dependencies in your classes rather than creating instances yourself. Angular’s DI system handles the creation and lifecycle of service instances.

Why Dependency Injection?

Loose Coupling

Components don’t need to know how to create dependencies

Testability

Easy to provide mock dependencies for testing

Reusability

Services can be shared across components

Maintainability

Change implementations without modifying consumers

The @Injectable Decorator

Mark a class as available for injection:
providedIn: 'root' makes the service available throughout the application as a singleton.

Provider Scopes

Angular supports several scopes for providing services:

Root Scope

Application-wide singleton:

Component Scope

Provide a service at the component level:

Platform Scope

Shared across multiple Angular applications on the same page:

Using inject() Function

The modern way to inject dependencies:
inject() can only be called during the construction phase (in constructors, initializers, or factory functions).

Constructor Injection (Legacy)

Traditional approach using constructor parameters:

Injection Tokens

Provide non-class dependencies:

Provider Types

Angular supports several provider configurations:

Class Provider

Value Provider

Factory Provider

Existing Provider

Alias one token to another:

Optional Dependencies

Handle missing dependencies gracefully:

Self and SkipSelf

Control dependency resolution:

Hierarchical Injection

Angular creates a hierarchy of injectors:
1

Platform Injector

Shared across all applications on the page
2

Root Injector

Application-wide services (providedIn: ‘root’)
3

Module Injectors

Created for lazy-loaded modules (legacy)
4

Component Injectors

Created when components have providers
5

Element Injectors

Created for directives with providers

Best Practices

  1. Use inject() over constructor injection for modern applications
  2. Provide in root for application-wide singletons
  3. Use component providers for component-specific state
  4. Leverage injection tokens for configuration and non-class dependencies
  5. Keep services focused on single responsibilities
  6. Avoid circular dependencies between services
  7. Use factory providers for complex initialization logic

Next Steps

Services

Learn to create and use services

Components

Understand component architecture