← Back to Articles         
What is Dependency Injection in Angular?

What is Dependency Injection in Angular?

Published on 30 Sep 2026     7 min read Angular
Services & Dependency Injection

1. What is Dependency Injection in Angular?

What is Dependency Injection?

Dependency Injection (DI) is a design pattern in which an object receives the dependencies it needs from an external system instead of creating those dependencies itself.

Angular has a built-in Dependency Injection framework.

For example, if a Product component needs ProductService, we should not manually create the service:

const service = new ProductService();

Instead, we ask Angular to provide it:

constructor(private productService: ProductService) {
}

Angular creates or retrieves the appropriate ProductService instance and injects it into the component.

Simple idea

Without DI

Product Component
      ↓
new ProductService()
      ↓
ProductService


With Angular DI

Product Component
      ↓
"I need ProductService"
      ↓
Angular Injector
      ↓
ProductService instance
      ↓
Injected into Component

2. Why do we need Dependency Injection?

Consider this component:

export class Product {
  private productService = new ProductService();
}

The component is directly responsible for creating ProductService.

This creates tight coupling.

If the service constructor later changes:

constructor(private http: HttpClient) {}

the component would also need to know how to create HttpClient and its dependencies.

DI solves this problem.

constructor(private productService: ProductService) {}

Now the component only says:

"I need a ProductService."

Angular handles how that service is created.

This creates loose coupling.


3. Full Simple Sample Program

product.service.ts

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class ProductService {

  getProducts(): string[] {
    return [
      'Laptop',
      'Mobile',
      'Keyboard'
    ];
  }
}

product.ts

import { Component } from '@angular/core';
import { ProductService } from './product.service';

@Component({
  selector: 'app-product',
  standalone: true,
  templateUrl: './product.html'
})
export class Product {

  products: string[] = [];

  constructor(private productService: ProductService) {
    this.products = this.productService.getProducts();
  }
}

product.html

<h2>Products</h2>

<ul>
  @for (product of products; track product) {
    <li>{{ product }}</li>
  }
</ul>

4. Program Explanation

Step 1 — Register the Service

@Injectable({
  providedIn: 'root'
})

This makes ProductService available through Angular's root-level dependency injection system.

Angular can now create and manage the service.


Step 2 — Request the Dependency

The component requests ProductService:

constructor(private productService: ProductService)

The component doesn't write:

new ProductService();

Instead, Angular handles the creation.


Step 3 — Angular Resolves the Dependency

Angular looks for a provider that can supply:

ProductService

Because we registered:

providedIn: 'root'

Angular knows how to create it.


Step 4 — Angular Injects the Service

Angular provides the service instance to:

private productService: ProductService

The component can then use:

this.productService.getProducts();

5. How does Angular's DI System Work?

Angular DI mainly involves three concepts:

Dependency
Provider
Injector

Dependency

A dependency is something another class requires.

Here:

constructor(private productService: ProductService)

ProductService is the dependency.


Provider

A provider tells Angular how to obtain or create a dependency.

For example:

@Injectable({
  providedIn: 'root'
})

registers ProductService with a root-level injector.

Conceptually, Angular has information equivalent to:

When someone asks for ProductService
            ↓
Create/provide ProductService

Providers can also be explicitly configured:

providers: [ProductService]

Injector

The Injector stores provider configuration, creates/resolves dependencies, and supplies them to consumers.

Conceptually:

Component
   │
   │ needs ProductService
   ▼
Injector
   │
   │ finds provider
   ▼
ProductService
   │
   │ creates/reuses instance
   ▼
Component receives instance

6. Complete DI Flow

Suppose Angular creates:

Product

and sees:

constructor(private productService: ProductService)

The flow is:

1. Angular creates Product component
             ↓
2. Angular detects ProductService dependency
             ↓
3. Angular asks the injector for ProductService
             ↓
4. Injector searches for a matching provider
             ↓
5. Provider tells Angular how to create ProductService
             ↓
6. Angular creates ProductService
   OR reuses an existing appropriate instance
             ↓
7. ProductService is injected into Product
             ↓
8. Product can use ProductService

So remember:

Component
   ↓ requests
Dependency
   ↓
Injector
   ↓ finds
Provider
   ↓ creates/resolves
Service
   ↓ injects
Component

7. Dependency Chain

The real power of DI becomes clearer when dependencies have their own dependencies.

Suppose:

Product Component
       ↓
ProductService
       ↓
HttpClient

The component only requests:

ProductService

Angular can resolve the entire dependency graph:

Product Component
       │
       ▼
ProductService
       │
       ▼
HttpClient

For example:

@Injectable({
  providedIn: 'root'
})
export class ProductService {

  constructor(private http: HttpClient) {
  }
}

Angular resolves HttpClient before creating ProductService.

This is called dependency resolution.


8. Modern Angular inject() Function

Constructor injection is common:

constructor(private productService: ProductService) {}

Modern Angular also provides the inject() function:

import { Component, inject } from '@angular/core';
import { ProductService } from './product.service';

@Component({
  selector: 'app-product',
  standalone: true,
  templateUrl: './product.html'
})
export class Product {

  private productService = inject(ProductService);

  products = this.productService.getProducts();
}

Both approaches use Angular's DI system.

Constructor Injection

constructor(private service: ProductService) {}


inject()

private service = inject(ProductService);

inject() is particularly useful in functional APIs such as guards, interceptors, provider factories, and field initializers.


9. Hierarchical Dependency Injection

Angular's DI system is hierarchical.

Services can be provided at different levels.

For example:

Application / Environment Injector
          ↓
      Component
          ↓
    Child Component

If you use:

@Injectable({
  providedIn: 'root'
})

the service is available application-wide.

If you provide it directly in a component:

@Component({
  selector: 'app-product',
  providers: [ProductService]
})

that component gets an instance from its own element injector, and descendants can resolve it from there unless a nearer provider overrides it.

This is important because where you provide a service affects its scope and instance sharing.


10. Advantages of Dependency Injection

Loose Coupling

The component doesn't need to know how the service is constructed.

Component → asks for interface/token/class
Angular DI → handles creation

Reusability

The same service can be injected into multiple components.

Component A ─┐
Component B ─┼── ProductService
Component C ─┘

Easier Testing

A real dependency can be replaced with a test implementation or mock.

Centralized Dependency Management

Angular manages dependency creation and resolution.

Maintainability

Changing how a service is constructed usually doesn't require changing every consumer.

Dependency Chaining

Angular can automatically resolve dependencies of dependencies.


11. Disadvantages

Learning Curve

Concepts such as:

  • Providers
  • Injection tokens
  • Injector hierarchy
  • Provider scopes

can initially be confusing.

Incorrect Provider Scope

Providing the same service at multiple levels can accidentally create multiple service instances.

Hidden Dependency Resolution

In large applications, complex dependency graphs can make it harder to understand exactly where a dependency originates.


12. Key Points

  • DI = Dependency Injection.
  • Angular has a built-in DI framework.
  • A dependency is an object/class another object requires.
  • A provider tells Angular how to supply a dependency.
  • An injector resolves and manages dependencies.
  • providedIn: 'root' commonly provides an application-wide service.
  • Components should generally request dependencies instead of manually creating them.
  • Angular supports both constructor injection and inject().
  • Angular DI is hierarchical.
  • Different provider locations can result in different service instances.
  • DI promotes loose coupling, maintainability, reuse, and testability.

13. Interview Questions and Answers

1. What is Dependency Injection in Angular?

Dependency Injection is a design pattern where Angular provides the dependencies required by a class instead of the class manually creating those dependencies.


2. Why do we need DI?

DI reduces tight coupling, improves code reuse, makes applications easier to maintain, and makes dependencies easier to replace during testing.


3. What are the main parts of Angular DI?

The main concepts are:

Dependency
Provider
Injector
Injection Token

4. What is an Injector?

An injector is responsible for finding providers, creating or retrieving dependency instances, and supplying those dependencies to consumers.


5. What is a Provider?

A provider tells Angular how to create or obtain a dependency.

For example:

providers: [ProductService]

6. What does providedIn: 'root' mean?

@Injectable({
  providedIn: 'root'
})

It makes the service available through the application's root-level DI environment and normally gives you a shared application-wide instance.


7. Constructor injection vs inject()?

Constructor injection:

constructor(private service: ProductService) {}

Modern inject():

private service = inject(ProductService);

Both resolve dependencies using Angular's DI system.


8. What is hierarchical DI?

Angular maintains injectors at different levels. A dependency can therefore be provided application-wide or more locally, such as at a component level.


9. What happens if Angular cannot find a provider?

Angular fails dependency resolution and typically throws a NullInjectorError indicating that no provider was found for the requested dependency.


Interview Definition

Dependency Injection in Angular is a mechanism where Angular's injector creates or retrieves dependencies based on registered providers and supplies them to components, services, directives, and other consumers, reducing tight coupling and improving maintainability and testability.

← Back to Articles