skip to content

From the browser console of an Angular app in development mode, how do you read and change a component's state with ng.getComponent and ng.applyChanges?

level: middleimportance: should knowfreq 32%

answer

  1. select the host element first
  2. the window.ng debug global
  3. plain field versus signal
  4. force a synchronous check

basics

~20 s

In development mode Angular publishes window.ng. Select the component's host element, call ng.getComponent($0) to get its instance, and change a field. A signal's set() schedules an update by itself; a plain field needs ng.applyChanges(instance) to mark it for check and run change detection.

solid answer

~40 s

Development builds publish debugging functions on the global `ng`. Pick the component's **host element** in the Elements panel (the browser exposes it as `$0`) and call `ng.getComponent($0)`; it returns the instance or `null` if that element is not a component host. For any element inside a template, `ng.getOwningComponent($0)` returns the component whose view contains it, and `ng.getContext($0)` returns the embedded view context inside `@for`/`@if` blocks. You can then read or change state. If the template reads a **signal**, calling `.set()` notifies Angular and the view updates on its own. A **plain field** assignment from the console is invisible to Angular, so call `ng.applyChanges(instance)`: it marks the component for check (which matters for OnPush, the v22 default) and synchronously runs change detection. `ng.getInjector($0).get(SomeService)` reaches services. None of this exists in optimized builds.

code

ts · 11 lines
ts
// In the browser console of an app served with `ng serve`
// 1. Select <app-cart> in the Elements panel ($0 is the selection)
const cart = ng.getComponent($0);

cart.items();                 // read a signal
cart.items.set([]);           // signal write: the view updates by itself

cart.note = 'debug';          // plain field: Angular is not notified
ng.applyChanges(cart);        // mark for check + synchronous change detection

ng.getOwningComponent($0.querySelector('button')); // component whose view holds the button

go deeper

for a junior

Remember the routine: select the host element, call ng.getComponent($0), change state, and call ng.applyChanges when the screen does not update.

for a middle

Explain why a plain field change is invisible (zoneless default, OnPush default, console code outside any zone) while a signal write updates the view by itself.

for a senior

Use getOwningComponent, getContext, getInjector and getListeners to confirm state, providers and event wiring in a live dev session before touching code.

for a principal

Build a team debugging practice around dev-mode tooling and decide where a non-optimized diagnostic environment is justified.

## The `ng` global When an Angular application runs in **development mode**, the framework publishes a set of debugging functions on `window.ng`. They exist for exactly one audience: a developer at the browser console. Production builds define `ngDevMode` as `false`, and the whole mechanism is compiled out, so `ng` is `undefined` there. The public functions include: | Function | Returns | | :-- | :-- | | `ng.getComponent(el)` | The component instance whose **host** is `el`, or `null` | | `ng.getOwningComponent(elOrDir)` | The component whose **view contains** the element | | `ng.getContext(el)` | The embedded-view context around `el` (inside `@for`, `@if`, `*ngFor`...), else the owning component | | `ng.getDirectives(node)` | Directive instances on the node | | `ng.getInjector(elOrDir)` | The injector at that element or directive | | `ng.getListeners(el)` | Event listeners Angular registered on the element | | `ng.getHostElement(instance)` | The host element of a component or directive | | `ng.getRootComponents(elOrDir)` | The root components of the application | | `ng.applyChanges(instance)` | Marks the component for check and runs change detection synchronously | Functions whose names start with `ɵ` are internal and are meant for tools like Angular DevTools, not for you. ## The inspect-and-modify routine 1. In the Elements panel, select the component's host element, for example `<app-cart>`. The browser exposes the selection as `$0`. 2. Get the instance: `const cart = ng.getComponent($0);` 3. Read state: `cart.items()` for a signal, `cart.discount` for a plain field. 4. Change state and let the view catch up (see below). 5. Reach a service: the easiest route is a field the component already holds (`cart.cartService`); `ng.getInjector($0)` returns the element's injector when you have a token reference to look it up with. A common stumble: selecting a `<div>` inside the component and calling `getComponent` returns `null`, because the `div` hosts no component. Use `getOwningComponent($0)` instead, or select the component's own tag. ## Why a plain field change does not show up Angular only re-renders a view when change detection runs and the view is eligible to be checked. - Since Angular 21 applications are **zoneless** by default: nothing patches browser APIs, and change detection is scheduled by notifications such as signal writes, template events, or `markForCheck()`. An assignment typed into the console notifies nobody. - Since Angular 22 components default to **OnPush**, so even when change detection runs, a component that was not marked dirty is skipped. - Even in an older zone-based app, code run from the console does not run inside Angular's zone, so it triggers nothing. `ng.applyChanges(cart)` solves all three: it marks the component's view dirty (the equivalent of `markForCheck`) and then synchronously runs change detection from the application's root components. ## Why a signal change does show up If the template reads `items()`, then `cart.items.set([...])` marks the views that read that signal for refresh and notifies Angular's scheduler, so the screen updates without `applyChanges`. This is a useful live demonstration of why signals fit zoneless, OnPush-by-default Angular. ## Other questions the console answers - **Which directives are on this element?** `ng.getDirectives($0)` lists them, which settles "is my directive even applied?" in one call. - **Is my `(click)` handler wired?** `ng.getListeners($0)` lists the listeners Angular registered, with their event names. - **Which item is this row?** Inside an `@for` block, `ng.getContext($0)` returns the embedded view's context, including the current item as `$implicit`. - **Which app does this belong to?** `ng.getRootComponents($0)` returns the root components, useful on pages with several Angular roots. ## Practical cautions - **Development only**: none of these functions exist in an optimized build. Code that must work in production cannot use them. - **Not an API for app code**: they are debugging aids; application code should use `ChangeDetectorRef`, signals and DI instead. - **Side effects are real**: calling methods from the console runs the app's own logic, including HTTP calls. - **Use Angular DevTools alongside**: its `$ng0` shortcut gives you the selected instance without hunting for the host element.

  • Why does ng.getComponent($0) return null when a plain div inside the component is selected?
    `getComponent` returns the component *hosted* on that element, and a `div` hosts none. Use `ng.getOwningComponent($0)` to get the component whose view contains the element, or `ng.getContext($0)` to get the surrounding embedded-view context, such as the current item inside an `@for` block.
  • Why is window.ng undefined on an Angular production build?
    Angular publishes the debug utilities only when `ngDevMode` is on. When the CLI optimizes scripts it defines `ngDevMode` as `false`, and the minifier removes the publishing code together with other dev-only checks. To use the utilities against a deployed environment, it must be built with optimization disabled.

saying these in an interview costs you the question

  • Assigning a plain field from the console re-renders the component automatically.
  • ng.getComponent works on any element inside the component's template.
  • window.ng is available in production for support staff to debug users' sessions.
  • ng.applyChanges only checks the given component and never its parents.
  • Application code should call ng.applyChanges instead of using ChangeDetectorRef.