In Angular queries, what does the read option change, and what do you get from viewChild('ref') without it?
answer
- locator finds, read chooses
- default depends on the node
- component instance vs host element
- ElementRef, TemplateRef, ViewContainerRef, Injector
basics
~20 sThe 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 sEvery 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
Remember that read chooses what you get back from the node the locator found, such as its ElementRef instead of the component.
Explain the default value for each locator kind and the four special tokens Angular creates on request.
Choose reads deliberately: prefer a component's own API over reaching for its host element, and use ViewContainerRef or TemplateRef reads for extension points.
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