skip to content

Init to Destroy Sequence

The order of ngOnChanges, ngOnInit, ngDoCheck, the after-content and after-view hooks and ngOnDestroy, plus afterNextRender and DestroyRef. Interviewers ask what runs when, and why.

part ofAngularoverview, primer and where to startread it →
on this pageshow

explore

questions

4

In an Angular component, what work belongs in the constructor and what belongs in ngOnInit, and why?

level: juniorimportance: must knowfreq 82%

answer

  1. when do bindings arrive?
  2. construction is the injection context
  3. first input values, then init
  4. required signal input read too early

basics

~20 s

The constructor runs when Angular creates the instance, before any parent bindings are applied, so it should only inject dependencies and register setup. ngOnInit runs once after the first input values are set, so input-dependent initialisation belongs there.

solid answer

~40 s

The constructor is plain TypeScript: Angular calls it when it instantiates the component, and at that moment the parent's bindings have not been applied, so inputs still hold their defaults and a required signal input throws `NG0950` if read. It is, however, an **injection context**, which makes it the place for `inject()`, `inject(DestroyRef).onDestroy(...)` and `afterNextRender(...)` registrations. `ngOnInit` runs exactly once, during the component's first change-detection pass, after the first `ngOnChanges` (when an input is bound) and before the component's own template is rendered. That makes it the place to start work that depends on initial input values, such as loading data for an `orderId` input. Keep the constructor cheap: no HTTP calls, no DOM work.

code

ts · 24 lines
ts
import { Component, DestroyRef, OnInit, inject, input, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-order-summary',
  template: `<p>Order {{ orderId() }}: {{ status() }}</p>`,
})
export class OrderSummary implements OnInit {
  private readonly http = inject(HttpClient);
  readonly orderId = input.required<string>();
  readonly status = signal('loading');

  constructor() {
    // Injection context: fine. Reading this.orderId() here would throw NG0950.
    inject(DestroyRef).onDestroy(() => console.log('summary destroyed'));
  }

  ngOnInit(): void {
    // The first bound value is available now.
    this.http
      .get<{ status: string }>(`/api/orders/${this.orderId()}`)
      .subscribe((order) => this.status.set(order.status));
  }
}

go deeper

for a junior

Recall that the constructor runs before parent bindings are applied and ngOnInit runs once after the first input values arrive, and that inject() belongs in construction.

for a middle

Explain the sequence: instantiate, apply bindings during the first check, ngOnChanges if inputs are bound, ngOnInit, then render the template. Name NG0950 and NG0203 as the two errors the wrong placement produces.

for a senior

Show judgment: keep construction cheap and testable, move eager input reads to ngOnInit, and prefer reactive derivations of signal inputs so most components need little initialisation code at all.

for a principal

Frame it as a codebase convention: where setup and cleanup live, how signal-based components shrink ngOnInit, and how a consistent rule simplifies reviews and migrations from decorator inputs.

## Two different moments in a component's life An Angular component is a TypeScript class that Angular instantiates for you. Two methods are involved in getting it ready, and they run at clearly different moments: - **The `constructor`** is the standard JavaScript class constructor. Angular calls it when it creates the instance, which happens while it builds the parent's view. - **`ngOnInit`** is an Angular **lifecycle hook**: a method Angular calls on your behalf during change detection, the pass in which Angular walks the component tree from the top and brings every binding up to date. It runs **exactly once**. Between those two moments Angular applies the parent's **input bindings** (the `[orderId]="..."` in the parent template). That single fact explains almost everything about where code belongs. ## What is ready at each point | Available? | constructor | ngOnInit | |---|---|---| | Dependencies via `inject()` | yes (injection context) | no, throws `NG0203` | | Parent-bound input values | no, defaults only | yes, first values set | | Required signal input | throws `NG0950` when read | readable | | The component's own rendered template | no | no, it renders after `ngOnInit` | | Host element (`ElementRef`) | yes | yes | The first `ngOnChanges` call, which only happens when at least one input is bound, runs before `ngOnInit`. Then `ngOnInit`, then Angular goes on to create and check the component's own template. ## What goes where Put in the **constructor** (or in field initializers, which run as part of construction): - `inject()` calls for services, tokens and `ElementRef`. - Registrations that need an injection context: `inject(DestroyRef).onDestroy(...)`, `afterNextRender(...)`, `effect(...)`. - Plain field defaults and derived signals such as `computed(...)` that read inputs lazily. Put in **`ngOnInit`**: - Work that reads the **initial input values** imperatively: starting a request for the `orderId` the parent passed in, seeding a form from an input, subscribing to something keyed by an input. - Initialisation you want to run only when the component actually takes part in change detection, not whenever the class is constructed. Keep out of **both**: DOM measurement or manipulation of the component's own template, which does not exist yet. That belongs after rendering (`afterNextRender`) or, for reading query results, `ngAfterViewInit`. ## Why the split matters 1. **Correctness.** Reading an input in the constructor silently gives you the default value with a decorator `@Input`, and throws `NG0950` ("Required input is accessed before a value is set") with `input.required()`. The docs state that inputs are guaranteed to be available from `ngOnInit` onward. 2. **Injection context.** `inject()` only works while the class is being created (constructor, field initializers, or inside `runInInjectionContext`). Calling it in `ngOnInit` throws `NG0203`. So the two hooks are not interchangeable in either direction. 3. **Testability and cost.** A constructor that fires HTTP calls makes the class expensive and side-effectful to instantiate. Moving that work to `ngOnInit` keeps construction trivial. ## Signal inputs change the style, not the rule With signal inputs (`input()`, stable since v20), a lot of code that used to live in `ngOnInit` becomes a `computed()` or another reactive derivation that reads the input when needed, so a modern component may have no `ngOnInit` at all. The underlying timing rule is unchanged: the constructor still runs before bindings arrive, and anything that reads an input **eagerly** must wait until `ngOnInit`. ## A quick test for any line of setup code Ask two questions about each statement you are about to write: 1. **Does it call `inject()` or something that needs an injection context?** Then it must run during construction: a field initializer or the constructor. 2. **Does it read an input's value right now, imperatively?** Then it must wait until `ngOnInit`, or be rewritten so it reads the input lazily, inside a `computed()` or the template. A statement that needs both, such as a request that uses an injected `HttpClient` and an input `id`, splits naturally: inject `HttpClient` into a field, then call it in `ngOnInit`. Neither answer ever points to the component's own DOM: that is not ready in either place, and belongs to the render callbacks that run after Angular has written the view to the page. ## Common mistakes - Starting a fetch in the constructor with an input value, then wondering why it requested `undefined`. - Calling `inject()` inside `ngOnInit` or an event handler. - Treating `ngOnInit` as "after the view is ready" and querying child elements there. - Expecting `ngOnInit` to re-run when an input changes; only `ngOnChanges` (or a signal derivation) reacts to later changes.

  • Why is inject() allowed in the constructor but not in ngOnInit?
    `inject()` only works inside an injection context: while the class is being constructed (constructor and field initializers) or inside `runInInjectionContext`. By the time `ngOnInit` runs, construction is over, so the call throws `NG0203`. Inject in a field or the constructor, keep the reference, and use it later.
  • Can ngOnInit read the component's own template elements?
    Not reliably. `ngOnInit` runs before the component's template is created and checked, so child elements and view query results are not ready yet. Read query results in `ngAfterViewInit`, and do DOM measurement or manipulation in `afterNextRender`, which runs after Angular has rendered the application to the DOM.

saying these in an interview costs you the question

  • The constructor is where you read values the parent passes as inputs.
  • ngOnInit runs again every time an input changes.
  • Calling inject() inside ngOnInit is fine because the component already exists.
  • By ngOnInit the component's template is rendered, so child elements can be queried.
  • Constructor and ngOnInit are interchangeable; using ngOnInit is only a style convention.
open as a page

In Angular, in what order do a component's lifecycle hooks run on first render, and which of them repeat on later checks?

level: middleimportance: must knowfreq 74%

basics

~10 s

First render: constructor, ngOnChanges (if inputs are bound), ngOnInit, ngDoCheck, ngAfterContentInit, ngAfterContentChecked, ngAfterViewInit, ngAfterViewChecked; ngOnDestroy runs once at removal. Later checks repeat ngOnChanges on input change, ngDoCheck and both Checked hooks.

open as a page

In Angular, what does injecting DestroyRef offer that implementing ngOnDestroy does not, and what decides when its callbacks run?

level: middleimportance: should knowfreq 52%

basics

~20 s

ngOnDestroy is one class method. DestroyRef is an injectable handle whose onDestroy() registers any number of callbacks next to their setup, even inside helper functions, and returns an unregister function; in a component it fires on that component's destruction, elsewhere on its injector's.

open as a page

An Angular chart component must measure its container after rendering: why use afterNextRender rather than ngAfterViewInit, and which phases?

level: seniorimportance: should knowfreq 46%

basics

~20 s

ngAfterViewInit runs mid change detection, per component, and also on the server. afterNextRender runs once after Angular has rendered every component to the DOM, only in the browser; measure in earlyRead and draw in write.

open as a page