intermediate

Dependency injection

Understand providers, injectors, tokens, service lifetimes, tree-shakable providers, and test replacement seams.

Angular DI resolves tokens through an injector tree: platform → root → route/component injectors. Providers bind a token (class or `InjectionToken`) to a factory, class, or value.

| Scope | Lifetime | |-------|----------| | root (`providedIn: 'root'`) | Application singleton | | Component providers | New instance per component subtree | | Route providers | Per lazy-loaded route |

`inject()` function works in constructors, factories, and field initializers. Tests override providers with `TestBed.configureTestingModule`.

On interviews: hierarchical injectors, why two components get different instances, and tree-shakable `providedIn`.

Common pitfalls: manual `new Service()` bypassing DI, wrong provider scope causing stale state, and opaque tokens without interfaces.

The trade-off is flexible injector trees versus tracing which component gets which instance during debugging.

Checklist:

  • Prefer inject() and constructor injection.
  • Match provider scope to state lifetime.
  • Use InjectionToken for config primitives.
  • Override providers in tests, not globals.