View and Content Queries
Read one Angular interview question at a time, then flip for the complete explanation, example, and code.
Read one Angular interview question at a time, then flip for the complete explanation, example, and code.
Angular Interview Questions · View and Content Queries Companion Article
Angular applications are built from components that often need to interact with child components, directives, DOM elements, and projected content. Angular provides view queries and content queries for finding and accessing these elements safely.
Modern Angular provides signal-based query APIs such as viewChild(), viewChildren(), contentChild(), and contentChildren(). These APIs integrate naturally with Angular Signals and make query results reactive.
Understanding these APIs is especially useful when building reusable UI components, working with forms, controlling child components, accessing template elements, or creating components that accept projected content.
A query allows an Angular component to obtain a reference to something that exists inside or around its template.
A query may locate:
Angular queries can broadly be divided into:
| Query Type | Searches |
|---|---|
| View Query | The component's own template |
| Content Query | Content projected into the component |
viewChild() | First matching view child |
viewChildren() | All matching view children |
contentChild() | First matching projected child |
contentChildren() | All matching projected children |
The difference between view and content is one of the most important ideas to understand before working with Angular queries.
A view query searches inside the template that belongs to the current component.
Suppose a parent component template contains another Angular component:
<app-profile></app-profile>
The parent component can access the ProfileComponent using a view query.
profile = viewChild(ProfileComponent);
The query searches the component's own view and returns a reactive signal containing the matching component when it is available.
View queries are commonly used for:
Child component:
import { Component } from '@angular/core';
@Component({
selector: 'app-counter',
template: `<p>Count: {{ count }}</p>`
})
export class CounterComponent {
count = 0;
increment(): void {
this.count++;
}
}
Parent component:
import { Component, viewChild } from '@angular/core';
import { CounterComponent } from './counter.component';
@Component({
selector: 'app-root',
imports: [CounterComponent],
template: `
<app-counter></app-counter>
<button (click)="increase()">Increase</button>
`
})
export class AppComponent {
counter = viewChild(CounterComponent);
increase(): void {
this.counter()?.increment();
}
}
Here:
CounterComponent.viewChild() searches the parent's view.this.counter() reads the query signal.increment() method.viewChild()viewChild() returns the first matching element, directive, or component from the component's own view.
Basic syntax:
child = viewChild(SomeComponent);
The result is a signal.
Therefore, you read the value by calling it:
this.child();
Not:
this.child;
This is an important difference between signal-based queries and older decorator-based query APIs.
viewChild() with a ComponentTemplate:
<app-user-profile></app-user-profile>
Component:
profile = viewChild(UserProfileComponent);
Access the result:
const profileComponent = this.profile();
Because a child may not always exist, the result can initially be undefined.
For example, consider conditional rendering:
@if (showProfile) {
<app-user-profile></app-user-profile>
}
When showProfile is false:
this.profile()
returns:
undefined
When the child appears, Angular updates the query automatically.
viewChildren()viewChildren() is used when multiple matching children may exist inside the component's template.
Example:
<app-card></app-card>
<app-card></app-card>
<app-card></app-card>
Query:
cards = viewChildren(CardComponent);
Reading the signal:
const cardComponents = this.cards();
The returned value represents all currently matching children.
Example:
showCount(): void {
console.log(this.cards().length);
}
Output:
3
viewChildren() QueriesSignal queries are particularly useful when the template changes dynamically.
@for (product of products(); track product.id) {
<app-product-card [product]="product"></app-product-card>
}
Query:
productCards = viewChildren(ProductCardComponent);
If the number of rendered products changes, Angular updates the query result.
This means query signals work naturally with Angular's reactive rendering model.
A content query searches content that another component passes into the current component.
Angular commonly calls this content projection.
Content projection uses:
<ng-content></ng-content>
Consider a reusable panel component:
<app-panel>
<app-panel-header></app-panel-header>
</app-panel>
The PanelHeaderComponent is written by the consumer of app-panel.
It does not originate from the panel's own template.
Therefore, viewChild() is not the correct query.
The panel can use:
header = contentChild(PanelHeaderComponent);
This distinction is essential.
Suppose the component is used like this:
<app-card>
<app-user-info></app-user-info>
</app-card>
And app-card contains:
<div class="card">
<app-card-header></app-card-header>
<ng-content></ng-content>
</div>
For CardComponent:
CardComponent
│
├── Own View
│ └── CardHeaderComponent
│
└── Projected Content
└── UserInfoComponent
Therefore:
viewChild(CardHeaderComponent)
can find CardHeaderComponent.
While:
contentChild(UserInfoComponent)
can find UserInfoComponent.
Remember:
View queries inspect what the component owns. Content queries inspect what the component receives.
contentChild()contentChild() retrieves the first matching child from projected content.
Example reusable container:
import { Component, contentChild } from '@angular/core';
@Component({
selector: 'app-panel',
template: `
<section>
<ng-content></ng-content>
</section>
`
})
export class PanelComponent {
title = contentChild(PanelTitleComponent);
}
Usage:
<app-panel>
<app-panel-title></app-panel-title>
</app-panel>
The PanelTitleComponent is projected into PanelComponent, so the content query can locate it.
contentChildren()contentChildren() retrieves multiple matching children from projected content.
Example:
<app-menu>
<app-menu-item>Home</app-menu-item>
<app-menu-item>Products</app-menu-item>
<app-menu-item>Contact</app-menu-item>
</app-menu>
Inside MenuComponent:
items = contentChildren(MenuItemComponent);
The number of projected menu items can then be obtained with:
const count = this.items().length;
The query remains reactive when the projected content changes.
Modern Angular query APIs return signals.
The following APIs are signal-based:
viewChild()
viewChildren()
contentChild()
contentChildren()
For example:
child = viewChild(ChildComponent);
child is not the actual component instance.
It is a signal containing the query result.
Read it with:
this.child()
Query signals provide several benefits.
When the template changes, Angular updates the query signal.
Example:
@if (showDetails()) {
<app-details></app-details>
}
Query:
details = viewChild(DetailsComponent);
When showDetails() becomes false, the child disappears and the query changes accordingly.
computed()A query can participate in Angular's reactive computations.
cardCount = computed(() => this.cards().length);
If the number of queried cards changes, cardCount is recalculated.
effect()You can react to query changes:
effect(() => {
const child = this.child();
if (child) {
console.log('Child is available');
}
});
This can be useful when behavior needs to run whenever a queried child appears or changes.
Choose the query based on how many matches you expect.
| Requirement | API |
|---|---|
| One child in component view | viewChild() |
| Multiple children in component view | viewChildren() |
| One projected child | contentChild() |
| Multiple projected children | contentChildren() |
Example:
toolbar = viewChild(ToolbarComponent);
buttons = viewChildren(ButtonComponent);
title = contentChild(CardTitleComponent);
items = contentChildren(MenuItemComponent);
One of the most common uses of queries is accessing a child component instance.
Child:
@Component({
selector: 'app-video-player',
template: `<video></video>`
})
export class VideoPlayerComponent {
play(): void {
console.log('Playing video');
}
pause(): void {
console.log('Video paused');
}
}
Parent:
player = viewChild(VideoPlayerComponent);
playVideo(): void {
this.player()?.play();
}
Template:
<app-video-player></app-video-player>
<button (click)="playVideo()">
Play
</button>
The parent obtains the child component instance and calls its public method.
Direct child access can be appropriate for imperative actions such as:
For ordinary application data flow, Angular inputs, outputs, models, signals, or shared services are usually better architectural choices.
For example, avoid using queries simply to constantly copy state between a parent and child when the same relationship could be represented declaratively with inputs and outputs.
Angular queries can also access native DOM elements.
Suppose the template contains:
<input #searchInput type="text">
The component can query the element using its template reference:
searchInput = viewChild<ElementRef<HTMLInputElement>>('searchInput');
The actual browser element is available through:
this.searchInput()?.nativeElement;
For example:
focusSearch(): void {
this.searchInput()?.nativeElement.focus();
}
ElementRefElementRef is an Angular wrapper around a native DOM element.
Import it from Angular core:
import { ElementRef } from '@angular/core';
A simplified mental model is:
ElementRef
↓
nativeElement
↓
Actual browser DOM element
Example:
input = viewChild<ElementRef<HTMLInputElement>>('username');
focusInput(): void {
this.input()?.nativeElement.focus();
}
nativeElementThe nativeElement property provides direct access to the underlying DOM element.
Example:
const element = this.input()?.nativeElement;
For an input element, the type may be:
HTMLInputElement
This allows browser APIs such as:
element.focus();
element.select();
element.scrollIntoView();
ElementRef CorrectlyInstead of using:
viewChild<ElementRef>('searchInput');
you can provide the underlying HTML element type:
viewChild<ElementRef<HTMLInputElement>>('searchInput');
This improves TypeScript support.
You then receive proper autocomplete for properties such as:
nativeElement.value
nativeElement.focus()
nativeElement.select()
For a button:
button = viewChild<ElementRef<HTMLButtonElement>>('submitButton');
For a div:
container = viewChild<ElementRef<HTMLDivElement>>('container');
Although ElementRef provides direct DOM access, it should not become the default way to update the UI.
Avoid code such as:
this.title()?.nativeElement.textContent = 'New Title';
when Angular binding can perform the same job:
<h2>{{ title }}</h2>
Similarly, avoid:
this.box()?.nativeElement.style.display = 'none';
when template control flow can represent the state:
@if (showBox()) {
<div>Content</div>
}
Angular's declarative APIs make application state easier to understand and maintain.
Direct DOM access is more appropriate when interacting with browser functionality or APIs that Angular does not directly abstract.
ElementRefBe careful when using nativeElement to insert arbitrary HTML.
Avoid taking untrusted user input and manually assigning it as HTML through native DOM APIs.
For application rendering, prefer Angular templates and binding mechanisms so Angular's normal security model remains involved.
A template reference variable provides a name for an element, component, or directive inside a template.
Syntax:
<input #username>
Here:
#username
creates a template reference named username.
It can be queried with:
username = viewChild<ElementRef<HTMLInputElement>>('username');
Template:
<input #emailInput type="email">
<button (click)="focusEmail()">
Focus Email
</button>
Component:
emailInput = viewChild<ElementRef<HTMLInputElement>>('emailInput');
focusEmail(): void {
this.emailInput()?.nativeElement.focus();
}
Flow:
#emailInput
↓
viewChild('emailInput')
↓
ElementRef
↓
nativeElement
↓
<input>
A template reference is not limited to native HTML elements.
Example:
<app-counter #counter></app-counter>
The reference can represent the component instance.
Query:
counter = viewChild<CounterComponent>('counter');
Then:
this.counter()?.increment();
The value represented by a template reference depends on what the reference is attached to.
read OptionSometimes the element that matches a query is not the exact object you want to retrieve.
Angular query APIs support reading a different token from the matching element.
For example:
input = viewChild('username', {
read: ElementRef
});
This asks Angular to locate the element associated with username and return its ElementRef.
This capability becomes especially useful when working with directives, providers, template references, and advanced reusable component APIs.
A normal single-result query may temporarily have no matching value.
Therefore:
child = viewChild(ChildComponent);
can produce undefined.
When your component design guarantees that the child must exist, Angular also supports required queries.
Example:
child = viewChild.required(ChildComponent);
Now the query is expected to have a matching value.
Similarly, a required content child can be declared with:
title = contentChild.required(CardTitleComponent);
Use required queries only when the component's structure genuinely guarantees the queried item exists.
Optional:
child = viewChild(ChildComponent);
Usage:
this.child()?.doSomething();
Required:
child = viewChild.required(ChildComponent);
Usage can be simpler because the query's type does not include undefined once the required query contract is satisfied:
this.child().doSomething();
Do not mark optional UI as required.
For example, if this component is conditionally rendered:
@if (showChild()) {
<app-child></app-child>
}
a normal query is generally more appropriate.
Queries automatically reflect the rendered template.
Example:
showMessage = signal(false);
message = viewChild(MessageComponent);
Template:
@if (showMessage()) {
<app-message></app-message>
}
Initially:
this.message()
may be:
undefined
After:
this.showMessage.set(true);
Angular renders MessageComponent, and the query receives the child.
This reactive behavior is an important advantage of signal-based queries.
viewChild() vs viewChildren()Consider:
<app-item></app-item>
<app-item></app-item>
<app-item></app-item>
Using:
item = viewChild(ItemComponent);
is intended for retrieving a single matching child.
Using:
items = viewChildren(ItemComponent);
retrieves the collection of matches.
Use the API that represents the actual component relationship rather than relying on a single query where multiple children are conceptually important.
contentChild() vs contentChildren()Consider projected content:
<app-list>
<app-list-item>Java</app-list-item>
<app-list-item>Angular</app-list-item>
</app-list>
One projected child:
item = contentChild(ListItemComponent);
Multiple projected children:
items = contentChildren(ListItemComponent);
For reusable list, tabs, menu, accordion, or navigation components, contentChildren() is often particularly useful.
Suppose developers use a tabs component like this:
<app-tabs>
<app-tab title="Profile">
Profile content
</app-tab>
<app-tab title="Settings">
Settings content
</app-tab>
</app-tabs>
The individual app-tab components are projected into app-tabs.
Therefore, TabsComponent can query them using:
tabs = contentChildren(TabComponent);
It can then determine how many tabs exist:
tabCount = computed(() => this.tabs().length);
This is a typical real-world use case for content queries.
Template:
<input
#search
type="search"
placeholder="Search products">
<button (click)="focusSearch()">
Search
</button>
Component:
search = viewChild<ElementRef<HTMLInputElement>>('search');
focusSearch(): void {
this.search()?.nativeElement.focus();
}
This is a reasonable use of direct DOM access because focus() is an imperative browser action.
Child:
export class FilterComponent {
reset(): void {
// Reset filter state
}
}
Parent query:
filter = viewChild(FilterComponent);
Parent method:
clearFilters(): void {
this.filter()?.reset();
}
Here, the parent controls a specific child behavior without manually manipulating the child's DOM.
Queries should not be treated as a way to bypass component design.
Suppose a child component contains:
<input #internalInput>
A parent should normally interact with the public API of the child rather than trying to reach deeply into its internal implementation.
A better child API might be:
focus(): void {
this.internalInput()?.nativeElement.focus();
}
Then the parent can use:
child = viewChild(CustomInputComponent);
focusInput(): void {
this.child()?.focus();
}
This keeps implementation details inside the child component.
Queries and inputs solve different problems.
Use an input when the parent needs to provide data to the child.
product = input<Product>();
Example:
<app-product [product]="selectedProduct"></app-product>
Use a view query when the parent needs a reference to the actual child component or related object.
productComponent = viewChild(ProductComponent);
In most situations, ordinary data should flow through inputs rather than queries.
Use outputs when the child needs to communicate an event to the parent.
Example:
saved = output<void>();
Parent:
<app-editor (saved)="handleSave()"></app-editor>
Do not replace normal child-to-parent event communication with repeated inspection of the child through viewChild().
| Feature | View Query | Content Query |
|---|---|---|
| Searches own template | Yes | No |
| Searches projected content | No | Yes |
| Single query API | viewChild() | contentChild() |
| Multiple query API | viewChildren() | contentChildren() |
| Can access components | Yes | Yes |
| Can access directives | Yes | Yes |
| Can work with template references | Yes | Yes, depending on projected structure |
| Reactive signal result | Yes | Yes |
A query result depends on whether Angular has created the corresponding view or projected content.
Older Angular code often accesses decorator-based view queries in lifecycle hooks such as:
ngAfterViewInit()
and content queries in:
ngAfterContentInit()
Signal queries provide a more reactive model because the application can respond when the query value becomes available or changes.
For example:
child = viewChild(ChildComponent);
constructor() {
effect(() => {
const currentChild = this.child();
if (currentChild) {
console.log('Child available');
}
});
}
The important idea is not to assume that an optional query always contains a value immediately during component construction.
Think of a query signal like this:
Template structure changes
↓
Angular evaluates the query
↓
Query signal changes
↓
computed()/effect()/template consumers react
This is different from treating a query as a one-time lookup.
One of the clearest query styles is querying directly by component type.
profile = viewChild(ProfileComponent);
This tells Angular:
Find the matching ProfileComponent in my view.
For multiple components:
profiles = viewChildren(ProfileComponent);
This approach is usually preferable when the child type itself represents what you need.
Sometimes you need a specific element instead.
Template:
<input #firstName>
<input #lastName>
Queries:
firstName = viewChild<ElementRef<HTMLInputElement>>('firstName');
lastName = viewChild<ElementRef<HTMLInputElement>>('lastName');
Using reference names makes it possible to distinguish otherwise similar elements.
Queries are also useful for locating directives.
Example:
highlight = viewChild(HighlightDirective);
This provides access to the directive instance rather than the native DOM element.
That is useful when your directive exposes public methods or state that another component legitimately needs.
Angular queries can retrieve values associated with providers available on matched elements.
This capability is useful in advanced component libraries where children expose behavior through dependency-injection tokens rather than requiring the parent to know a concrete implementation class.
For normal application code, querying components and directives directly is usually easier to understand.
Content query behavior is affected by the projected content hierarchy.
When designing reusable components, consider whether the component should work only with directly projected children or should discover matching descendants deeper in projected content.
This matters for APIs such as tabs, menus, trees, accordions, and composite form controls.
Avoid making an overly broad query unless the component genuinely needs to manage nested descendants.
Incorrect idea:
item = viewChild(ProjectedItemComponent);
when the component comes from:
<app-container>
<app-projected-item></app-projected-item>
</app-container>
The item belongs to projected content.
Use:
item = contentChild(ProjectedItemComponent);
Incorrect:
this.child.doSomething();
With signal-based queries, read the signal:
this.child()?.doSomething();
For a guaranteed required query:
this.child().doSomething();
Consider:
@if (loggedIn()) {
<app-user-menu></app-user-menu>
}
The following query may have no match:
menu = viewChild(UserMenuComponent);
Therefore, code should account for absence:
this.menu()?.open();
Avoid turning Angular components into manual DOM scripts.
For example, avoid using ElementRef for routine UI state such as:
element.style.color = 'red';
element.style.display = 'none';
element.textContent = 'Updated';
when Angular bindings can represent the same state.
Prefer:
<p [class.error]="hasError()">
{{ message() }}
</p>
Use direct DOM access primarily when an imperative browser API is actually required.
If a parent frequently calls many internal methods of a child through:
viewChild()
the two components may become tightly coupled.
For example:
this.child()?.load();
this.child()?.validate();
this.child()?.calculate();
this.child()?.save();
this.child()?.refresh();
This can indicate that responsibilities should be reconsidered.
Depending on the use case, inputs, outputs, signals, services, or a clearer public component API may produce cleaner architecture.
Avoid:
child = viewChild(UserComponent);
getUserName(): string {
return this.child()?.userName ?? '';
}
simply because the parent needs user data.
If the parent owns the data, pass it through an input.
If the child produces an event, expose it through an output.
Queries are best suited for obtaining actual references rather than replacing normal Angular data flow.
Template reference names are strings:
<input #username>
Correct:
username = viewChild('username');
A mismatch such as:
viewChild('userName')
will not find #username.
The names must match.
Use this decision process:
What do you need to access?
|
+-- Something inside my component template?
| |
| +-- One item → viewChild()
| |
| +-- Multiple items → viewChildren()
|
+-- Something projected using ng-content?
|
+-- One item → contentChild()
|
+-- Multiple items → contentChildren()
viewChild()Use viewChild() when:
Example:
dialog = viewChild(DialogComponent);
viewChildren()Use viewChildren() when:
Example:
rows = viewChildren(TableRowComponent);
contentChild()Use contentChild() when:
Example:
header = contentChild(CardHeaderComponent);
contentChildren()Use contentChildren() when:
Example:
tabs = contentChildren(TabComponent);
Consider a reusable accordion API:
<app-accordion>
<app-accordion-item title="Account">
Account information
</app-accordion-item>
<app-accordion-item title="Security">
Security settings
</app-accordion-item>
<app-accordion-item title="Billing">
Billing information
</app-accordion-item>
</app-accordion>
The accordion items are content supplied by the consumer.
Therefore:
items = contentChildren(AccordionItemComponent);
is appropriate.
The accordion can calculate:
itemCount = computed(() => this.items().length);
It might also coordinate public methods exposed by its projected child components.
This pattern is common in reusable Angular component libraries.
Template:
<input #couponCode value="WELCOME10">
<button (click)="selectCode()">
Select Code
</button>
Component:
couponCode = viewChild<ElementRef<HTMLInputElement>>('couponCode');
selectCode(): void {
this.couponCode()?.nativeElement.select();
}
This is another practical example where accessing the native browser element is justified.
Suppose you have:
<app-dialog></app-dialog>
Instead of locating internal dialog HTML and changing its styles manually, expose a public component method:
export class DialogComponent {
open(): void {
// Manage dialog state
}
}
Parent:
dialog = viewChild(DialogComponent);
showDialog(): void {
this.dialog()?.open();
}
This preserves component encapsulation and reduces dependencies on internal markup.
The modern APIs follow a simple naming convention.
view + Child → viewChild()
view + Children → viewChildren()
content + Child → contentChild()
content + Children → contentChildren()
So you only need to answer two questions:
Use APIs such as:
viewChild()
viewChildren()
contentChild()
contentChildren()
when building new Angular applications using the modern signal-based approach.
Expose only meaningful public behavior from child components.
Inputs, outputs, signals, and services should continue to handle most application data flow.
Use safe access where appropriate:
this.child()?.run();
child = viewChild.required(ChildComponent);
should reflect a genuine component contract.
Prefer:
ElementRef<HTMLInputElement>
over untyped DOM access.
nativeElement usageUse Angular bindings whenever the same behavior can be represented declaratively.
A parent should use the child's public component API instead of manipulating the child's internal DOM.
| Requirement | Recommended API |
|---|---|
| Access one child component | viewChild() |
| Access multiple child components | viewChildren() |
| Access one projected component | contentChild() |
| Access multiple projected components | contentChildren() |
| Access a DOM element | viewChild() + ElementRef |
Access element by #reference | viewChild('reference') |
| Require a view child | viewChild.required() |
| Require a projected child | contentChild.required() |
viewChild() retrieves a single item from the component view.viewChildren() retrieves multiple items from the component view.contentChild() retrieves a single projected item.contentChildren() retrieves multiple projected items.ElementRef provides access to an underlying native DOM element.nativeElement should be used carefully and only when direct browser interaction is genuinely necessary.#searchInput can be used as query locators.Remember the four-query rule:
Own template + one child = viewChild()
Own template + many children = viewChildren()
Projected content + one child = contentChild()
Projected content + many = contentChildren()
And remember the signal rule:
child = viewChild(ChildComponent);
Read it with:
this.child();
For native elements:
Template Reference
↓
viewChild()
↓
ElementRef
↓
nativeElement
↓
Browser DOM Element
These concepts form the foundation for working with child references, reusable component APIs, content projection, and controlled DOM interaction in modern Angular applications.