Skip to content

Dependency Injection

Dependency Injection (DI) is a pattern where a class declares what it needs (its dependencies) instead of creating them itself, and a framework-managed injector supplies them. Angular’s DI system provides services to components, directives, and other services automatically based on constructor parameters (or the modern inject() function), which makes testing easier (swap in fakes) and avoids tightly-coupled, hard-to-reuse code.

@Injectable({ providedIn: 'root' }) // registered once, shared app-wide (singleton)
export class AuthService {
isLoggedIn(): boolean { return true; }
}
@Component({ selector: 'app-dashboard', template: `...` })
export class DashboardComponent {
// Constructor injection -- Angular supplies an AuthService instance automatically
constructor(private auth: AuthService) {}
ngOnInit() {
console.log(this.auth.isLoggedIn());
}
}
// Modern functional style (Angular 14+), often used outside constructors too
import { inject } from '@angular/core';
@Component({ selector: 'app-dashboard', template: `...` })
export class DashboardComponent {
private auth = inject(AuthService);
}

Instantiating a service manually with new instead of letting Angular inject it β€” this bypasses the DI system entirely, giving you a disconnected instance that doesn’t share state with the rest of the app and can’t be swapped out in tests.

export class DashboardComponent {
private auth = new AuthService(); // bypasses DI -- own private instance, not the shared singleton
}
// Correct: let Angular provide the shared instance
export class DashboardComponent {
private auth = inject(AuthService); // gets the SAME instance every other component uses
}
  1. What does providedIn: 'root' on @Injectable() mean?

    AnswerThe service is registered app-wide as a singleton β€” Angular creates one shared instance the first time it's needed, and every component/service that injects it gets that same instance.
  2. Why is manually calling new AuthService() inside a component considered a mistake?

    AnswerIt bypasses Angular's DI system, creating a disconnected instance rather than the shared singleton β€” this breaks assumptions about shared state and makes the component harder to test (you can't substitute a mock).
  3. What’s the modern alternative to constructor injection introduced in Angular 14+?

    AnswerThe inject() function, which can retrieve a dependency from anywhere in an injection context (constructors, field initializers, and certain other Angular APIs), not just as a constructor parameter.