Skip to the solution
HardFrontend9 min read

What is runInInjectionContext and when do you need to use it?

415 views

The problem

Question presented to candidate: "A developer moved a toSignal() call from a field into a click handler and the app now throws NG0203. Explain what an injection context is, what runInInjectionContext does, and when you would reach for it instead of restructuring the code."

What a strong answer should cover:

  • An injection context is the synchronous window in which inject() knows which injector to use: constructors, field initialisers, factory functions (useFactory, providedIn factories), functional guards, resolvers and interceptors.
  • inject(), toSignal(), toObservable(), effect(), takeUntilDestroyed() and custom injectX() helpers all require one; outside it they throw NG0203.
  • runInInjectionContext(injector, fn) runs fn synchronously as if inside that injector's context; it does not survive a setTimeout or an await inside fn.
  • The injector you pass decides lifetime: a component's Injector cleans up with the component; an EnvironmentInjector (root) lives with the app.
  • Prefer alternatives first: move the call to a field, or pass { injector } / a DestroyRef to APIs that accept it. Use runInInjectionContext for library code, dynamic setup, and tests (TestBed.runInInjectionContext).
  • assertInInjectionContext(fn) to give your own helpers the same clear error.

Clarifying questions expected:

  • "Does the work really have to start in the handler, or could it be created up front and triggered later?"
  • "Should what it creates live as long as the component, or as long as the app?"

Code / implementation expected: Yes β€” an injectX() helper that fails from a click handler with NG0203 and works through runInInjectionContext.

  • di
  • advanced

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

9 min read Β· 10 sections

Read the code

AngularCalling an inject helper from a click handler: NG0203, then fixed with runInInjectionContext
import { Component, Injectable, Injector, Signal, assertInInjectionContext, inject, runInInjectionContext, signal } from "@angular/core";
import { toSignal } from "@angular/core/rxjs-interop";
import { interval, map } from "rxjs";

@Injectable({ providedIn: "root" })
export class Clock {
  ticks = interval(500).pipe(map((i) => i + 1));
}

// A reusable "inject function": only valid where inject() is valid.
export function injectUptime(): Signal<number> {
  assertInInjectionContext(injectUptime);
  return toSignal(inject(Clock).ticks, { initialValue: 0 });
}

@Component({
  selector: "app-root",
  template: `
    <button (click)="startWrong()">Start timer (no context)</button>
    <button (click)="startRight()">Start timer (runInInjectionContext)</button>
    <p>{{ message() }}</p>
    @if (uptime(); as up) { <p>ticks: {{ up() }}</p> }
  `,
})
export class AppComponent {
  private injector = inject(Injector); // capture the injector while we are still in context
  message = signal("Not started");
  uptime = signal<Signal<number> | null>(null);

  startWrong() {
    try {
      this.uptime.set(injectUptime()); // a click handler is NOT an injection context
    } catch (e: any) {
      this.message.set(e.message.split(".")[0]);
    }
  }

  startRight() {
    // Re-enter this component's injection context, so inject(), toSignal() and the
    // DestroyRef cleanup all work, and the subscription ends when the component does.
    this.uptime.set(runInInjectionContext(this.injector, () => injectUptime()));
    this.message.set("Started inside runInInjectionContext");
  }
}

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 108 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 nextHard

What are Angular Signals?

Continue