skip to content

In Angular queries, what does the read option change, and what do you get from viewChild('ref') without it?

level: middleimportance: should knowfreq 42%

answer

  1. locator finds, read chooses
  2. default depends on the node
  3. component instance vs host element
  4. ElementRef, TemplateRef, ViewContainerRef, Injector

basics

~20 s

The locator decides which node matches; read decides what value is returned from that node's injector. Without read, a string locator gives the component or exportAs directive it names, else an ElementRef or, on ng-template, a TemplateRef.

solid answer

~40 s

Every Angular query has a **locator** (a class, a provider token, or a template reference variable name) and an optional `read`. The locator selects the matching node; `read` picks which value to take from that node's injector. Without `read`, a class locator returns that class's instance, and a string locator returns what the reference variable points at: the component on the element, the directive named by `#x="exportName"`, an `ElementRef` for a plain element, or a `TemplateRef` for an `<ng-template>`. With `read` you override that: `{read: ElementRef}` gets a component's host element, `{read: ViewContainerRef}` gets an anchor to insert views, `{read: TemplateRef}` gets a template, `{read: Injector}` the node injector, or any class or token provided on that node.

go deeper

for a junior

Remember that read chooses what you get back from the node the locator found, such as its ElementRef instead of the component.

for a middle

Explain the default value for each locator kind and the four special tokens Angular creates on request.

for a senior

Choose reads deliberately: prefer a component's own API over reaching for its host element, and use ViewContainerRef or TemplateRef reads for extension points.

for a principal

Decide which extension points a component library exposes through template reads, since they become public contracts consumers write markup against.

## Locator versus read An Angular query answers two questions, and it is easy to conflate them: 1. **Which node?** The **locator**, the first argument, decides that. It can be a component or directive class, any `ProviderToken` such as an `InjectionToken`, or a string that names a template reference variable. CSS selectors are not supported. 2. **What value from that node?** The `read` option decides that. It names a token to resolve from the matched node's injector. When `read` is omitted, the locator also decides the value, which is convenient but sometimes surprising. ## What you get without read | Locator | Matched node | Value returned | |---|---|---| | a component class, e.g. `viewChild(Chart)` | element hosting `Chart` | the `Chart` instance | | a directive class | element carrying the directive | the directive instance | | a provider token | node whose injector provides it | the provided value | | `'ref'` on a plain element `<div #ref>` | the div | an `ElementRef` | | `'ref'` on a component element `<app-chart #ref>` | the chart's host | the **component instance** | | `'ref'` with an export `<input #ref="ngModel">` | the input | the exported directive | | `'ref'` on `<ng-template #ref>` | the template | a `TemplateRef` | The fifth row is the common surprise: people write `viewChild<ElementRef>('chart')` on a component element and get the component, so `nativeElement` is `undefined`. The generic type is only an annotation; it does not change what Angular returns. ## What read can ask for - **`ElementRef`**: the node's host element, for example the host of a component when you need to measure it. - **`TemplateRef`**: the template of an `<ng-template>`, or of an element carrying a structural directive. - **`ViewContainerRef`**: a container anchored at that node, used to insert views or components beside it. - **`Injector`**: the node injector as seen from that position, useful for passing context into dynamically rendered templates. - **A class or token**: any directive, component or provider available on that node, for example `{read: NgModel}` to get the form directive on an element found by some other locator. `ElementRef`, `TemplateRef`, `ViewContainerRef` and `Injector` are handled specially: Angular creates them for the node on request. Other tokens are resolved from the directives and providers on that node. ## Examples ```ts import { Component, Directive, ElementRef, TemplateRef, ViewContainerRef, contentChild, viewChild, } from '@angular/core'; @Directive({selector: '[appRowTpl]'}) export class RowTpl {} @Component({selector: 'app-chart', template: `<canvas></canvas>`}) export class Chart {} @Component({ selector: 'app-dashboard', imports: [Chart], template: ` <app-chart #main /> <ng-container #slot /> `, }) export class Dashboard { chart = viewChild.required<Chart>('main'); // component instance chartHost = viewChild.required('main', {read: ElementRef}); // its host element slot = viewChild.required('slot', {read: ViewContainerRef}); // insertion point rowTpl = contentChild(RowTpl, {read: TemplateRef}); // template, not directive } ``` The same locator `'main'` yields two different values depending on `read`. For `rowTpl`, the locator finds the `<ng-template appRowTpl>` a parent projected in, and `read` swaps the directive instance for its `TemplateRef`. ## Where each is typically used - `ElementRef` reads: measuring, focusing, or handing a node to a non-Angular library. Prefer a component method where the child can expose one. - `TemplateRef` reads: a table or list component that lets consumers supply a row template. - `ViewContainerRef` reads: a host that creates components from code at a chosen place in its template. - `Injector` reads: rendering a projected template so that directives in it can inject what a wrapper component provides. ## Mistakes that come from mixing the two up - Using `read` to try to narrow which element matches. It never filters; if the locator matches three nodes, `viewChildren` still returns three values. - Expecting `{read: TemplateRef}` to work on an ordinary element. It resolves only where a template exists, such as an `<ng-template>` or an element carrying a structural directive. - Reading `ElementRef` on a component to call its methods. The host element has none of the component's API; query the component class instead. - Assuming a type argument converts the result. `viewChild<ElementRef>('x')` is a claim about the type, not an instruction to Angular. ## Signals and decorators alike `read` works the same on `viewChild()`, `viewChildren()`, `contentChild()`, `contentChildren()` and on the four decorators. With signal queries, supplying `read` also changes the signal's type: `viewChild('main', {read: ElementRef})` is a `Signal<ElementRef | undefined>`.

  • Why does viewChild<ElementRef>('chart') on <app-chart #chart> give an object without nativeElement?
    A reference variable on a component element points at the component instance, so that is what the query returns; the `ElementRef` generic is only a type annotation. Add `{read: ElementRef}` to get the host element instead.
  • Can a query read a service that a component provides in its providers array?
    Yes, if it is declared on the matched node itself: `read` resolves tokens from the directives and providers on that node, not from ancestors. A provider token can also serve as the locator, matching the node that provides it.

saying these in an interview costs you the question

  • The generic type parameter decides what the query returns
  • read changes which element the query matches
  • A string locator always returns an ElementRef
  • read accepts a CSS selector to narrow the match
  • Only ElementRef and TemplateRef can be read from a node