What is Dependency?
Dependency Example:
- A component (like a button on a web page) might depend on a styling library to make it look good.
- A service that handles user login might depend on a database library to interact with the database.
What is Circular Dependency?
- Alice says, "I can't start my project until Bob gives me his budget report."
- Bob says, "I can't finish my budget report until Alice starts her project, so I can get the final numbers."
- UserService needs to call a method in PaymentService to process a transaction.
- PaymentService needs to access a user's details, which it gets by calling a method in UserService.
Example of Circular Dependency in Angular.
// user.service.ts import { Injectable } from '@angular/core'; import { PaymentService } from './payment.service'; @Injectable({ providedIn: 'root' }) export class UserService { constructor(private paymentService: PaymentService) {} getUserDetails(userId: number) { console.log("Fetching user details for", userId); // Oh no ❌ UserService is calling PaymentService this.paymentService.getPaymentsForUser(userId); } }
// payment.service.ts import { Injectable } from '@angular/core'; import { UserService } from './user.service'; @Injectable({ providedIn: 'root' }) export class PaymentService { constructor(private userService: UserService) {} getPaymentsForUser(userId: number) { console.log("Fetching payments for", userId); // Oh no ❌ PaymentService is calling UserService again this.userService.getUserDetails(userId); } }
Error: Circular dependency in DI detected for UserService
Method 1: Resolve Circular Dependency Using Shared Service.
// data.service.ts @Injectable({ providedIn: 'root' }) export class DataService { getUserDetails(userId: string) { console.log("Fetching user details:", userId); } getPaymentsForUser(userId: string) { console.log("Fetching payments for user:", userId); } }
// user.service.ts @Injectable({ providedIn: 'root' }) export class UserService { constructor(private data: DataService) {} getUserDetails(userId: string) { this.data.getUserDetails(userId); } }
// payment.service.ts @Injectable({ providedIn: 'root' }) export class PaymentService { constructor(private data: DataService) {} getPaymentsForUser(userId: string) { this.data.getPaymentsForUser(userId); } }
Method 2: Use forwardRef().
In Angular, when you create a service or class, Angular tries to resolve all dependencies immediately at runtime.
But in circular dependency cases, one class might not be defined yet when Angular tries to use it.
👉 forwardRef() is a helper function that tells Angular:
“Don’t try to resolve this dependency right now — wait until everything is defined, and then resolve it.”
Fix Example:
// user.service.ts @Injectable({ providedIn: 'root' }) export class UserService { constructor( @Inject(forwardRef(() => PaymentService)) private payment: PaymentService ) {} getUserDetails(userId: string) { console.log("Fetching user details:", userId); this.payment.getPaymentsForUser(userId); } }
// payment.service.ts @Injectable({ providedIn: 'root' }) export class PaymentService { constructor( @Inject(forwardRef(() => UserService)) private user: UserService ) {} getPaymentsForUser(userId: string) { console.log("Fetching payments for user:", userId); this.user.getUserDetails(userId); } }
Trends is an amazing magazine Blogger theme that is easy to customize and change to fit your needs.