Skip to the solution
MediumFrontend10 min read

How does Dependency Injection work in Angular?

452 views

The problem

Question presented to candidate: "Our components never call new on the services they use, yet they always get a working instance. Walk me through what Angular's dependency injection is actually doing behind the scenes."

What a strong answer should cover:

  • Dependency injection means a class declares what it needs and something else builds and hands it over; in Angular that something is an injector.
  • A provider is the recipe (token to "how to make it"); @Injectable({ providedIn: 'root' }), providers: [...] arrays and provideX() functions all register providers.
  • Injectors form a tree: element injectors (from component and directive providers) are searched first, then environment injectors (root, route, platform), ending in a null injector that throws.
  • Instances are created lazily and cached per injector, so a root service is a singleton and is never built if nobody asks for it.
  • Dependencies are requested with inject() (or constructor parameters, which the compiler turns into the same calls).
  • A missing provider fails with NG0201 ("No provider found for ..."), including the dependency path.

Clarifying questions expected:

  • "Do you want the mental model, or the compiled output too?"
  • "Standalone app or NgModule-based? The injector tree is the same, but where providers are registered differs."

Code / implementation expected: Optional — a service that depends on another service, injected into a component, plus what happens when a provider is missing.

  • di
  • services

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

10 min read · 10 sections

Read the code

AngularInjectors build a dependency chain lazily, and fail with NG0201 for a missing provider
import { Component, Injectable, Injector, inject, signal } from "@angular/core";

const log: string[] = [];

// A dependency of a dependency: Angular builds the whole chain for you.
@Injectable({ providedIn: "root" })
export class ClockService {
  constructor() { log.push("ClockService constructed"); }
  now() { return "09:30"; }
}

@Injectable({ providedIn: "root" })
export class GreetingService {
  private clock = inject(ClockService); // GreetingService asks for ClockService
  constructor() { log.push("GreetingService constructed"); }
  greet(name: string) { return `Hello ${name}, it is ${this.clock.now()}`; }
}

// Registered in root, but nobody injects it, so it is never constructed.
@Injectable({ providedIn: "root" })
export class UnusedService {
  constructor() { log.push("UnusedService constructed"); }
}

// No @Injectable and no provider anywhere.
export class NotProvided {}

@Component({
  selector: "app-root",
  template: `
    <p>{{ message }}</p>
    <button (click)="tryMissing()">ask for a class nobody provides</button>
    <ol>
      @for (line of lines(); track $index) { <li>{{ line }}</li> }
    </ol>
  `,
})
export class AppComponent {
  private greeter = inject(GreetingService); // the component asks; it never calls new
  private injector = inject(Injector);
  message = this.greeter.greet("Ada");
  lines = signal([...log]);

  tryMissing() {
    try {
      this.injector.get(NotProvided);
    } catch (e) {
      log.push((e as Error).message.split("\n")[0]);
    }
    this.lines.set([...log]);
  }
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 57 of 121 in Angular

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Angular questions
Up nextMedium

What is the difference between constructor-based DI and the inject() function?

Continue