What Are View Queries Such as ViewChild?
What is a View Query?
A view query allows an Angular component to obtain a reference to something inside its own template/view.
For example, a parent template contains:
<app-counter></app-counter>
The parent may need direct access to that Counter component instance.
A view query allows Angular to find it.
Parent Component
│
│ View Query
▼
Parent Template
│
└── Counter Child
│
▼
Counter instance
Common view-query APIs include:
viewChild()
viewChildren()
and the traditional decorator APIs:
@ViewChild()
@ViewChildren()
What is ViewChild?
ViewChild allows a component to get a reference to one matching element, directive, or child component from its own view.
A common example is:
Parent directly calls a method on its child component.
For example:
Counter Component
│
├── Increment
└── Reset
Parent
│
└── Calls child.reset()
Why Do We Need View Queries?
Normally, components should communicate through:
Parent ── input() ──► Child
Parent ◄─ output() ── Child
But sometimes the parent needs direct access to something in its view.
Examples:
- Call a child component method.
- Focus an input element.
- Access a DOM element.
- Read a directive instance.
- Access multiple child components.
- Interact with a third-party UI component.
That's where view queries are useful.
Full Code Example – Modern viewChild()
Let's create a parent that directly resets a child counter.
counter.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-counter',
templateUrl: './counter.html'
})
export class Counter {
count = 0;
increment() {
this.count++;
}
reset() {
this.count = 0;
}
}
counter.html
<h2>Count: {{ count }}</h2>
<button (click)="increment()">
Increment
</button>
app.ts
import {
Component,
viewChild
} from '@angular/core';
import { Counter } from './counter/counter';
@Component({
selector: 'app-root',
imports: [Counter],
templateUrl: './app.html'
})
export class App {
counter = viewChild(Counter);
resetCounter() {
this.counter()?.reset();
}
}
app.html
<h1>Counter Application</h1>
<app-counter></app-counter>
<br>
<button (click)="resetCounter()">
Reset Counter
</button>
Code Explanation
The parent template contains:
<app-counter></app-counter>
Therefore, Counter is part of the parent's view.
The parent creates a view query:
counter = viewChild(Counter);
Angular searches the parent's view for the matching Counter component.
Conceptually:
App
│
│ viewChild(Counter)
│
▼
App Template
│
└── <app-counter>
│
▼
Counter instance
The parent can then access the child:
this.counter()
and call:
this.counter()?.reset();
Therefore:
Parent
│
│ viewChild()
▼
Counter
│
│ reset()
▼
count = 0
Why counter()?
Modern:
counter = viewChild(Counter);
returns a signal-based query result.
Therefore, you read the current result using:
this.counter()
Since the child might not currently exist, the result can be undefined.
That's why the example uses:
this.counter()?.reset();
Required viewChild()
If you know the queried child must exist, modern Angular also supports a required query.
Full Example
import {
Component,
viewChild
} from '@angular/core';
import { Counter } from './counter/counter';
@Component({
selector: 'app-root',
imports: [Counter],
template: `
<app-counter></app-counter>
<button (click)="resetCounter()">
Reset
</button>
`
})
export class App {
counter = viewChild.required(Counter);
resetCounter() {
this.counter().reset();
}
}
Now:
this.counter()
is expected to provide the Counter instance.
Traditional @ViewChild()
Existing Angular applications commonly use the decorator-based @ViewChild() API.
Full Code
counter.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-counter',
template: `
<h2>Count: {{ count }}</h2>
<button (click)="increment()">
Increment
</button>
`
})
export class Counter {
count = 0;
increment() {
this.count++;
}
reset() {
this.count = 0;
}
}
app.ts
import {
Component,
ViewChild
} from '@angular/core';
import { Counter } from './counter/counter';
@Component({
selector: 'app-root',
imports: [Counter],
template: `
<app-counter></app-counter>
<button (click)="resetCounter()">
Reset
</button>
`
})
export class App {
@ViewChild(Counter)
counter!: Counter;
resetCounter() {
this.counter.reset();
}
}
Modern vs Traditional
Modern Angular:
counter = viewChild(Counter);
Access:
this.counter()
Traditional:
@ViewChild(Counter)
counter!: Counter;
Access:
this.counter
So remember:
| Traditional | Modern |
|---|---|
@ViewChild() |
viewChild() |
| Decorator query | Signal-based query |
this.counter |
this.counter() |
| Common in existing applications | Preferred modern API |
Accessing an HTML Element
View queries aren't limited to Angular components.
They can also access DOM elements.
For example, suppose you want to focus an input box.
Full Code Example
app.ts
import {
Component,
ElementRef,
viewChild
} from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.html'
})
export class App {
nameInput =
viewChild<ElementRef<HTMLInputElement>>('nameInput');
focusInput() {
this.nameInput()?.nativeElement.focus();
}
}
app.html
<input
#nameInput
type="text"
placeholder="Enter your name">
<button (click)="focusInput()">
Focus Input
</button>
Code Explanation
The template contains:
#nameInput
This is a template reference variable.
The view query searches for it:
viewChild<ElementRef<HTMLInputElement>>('nameInput');
Then:
this.nameInput()
returns the queried reference.
ElementRef gives access to the native DOM element:
.nativeElement
Then:
.focus()
focuses the textbox.
Flow:
#nameInput
│
▼
viewChild('nameInput')
│
▼
ElementRef
│
▼
nativeElement
│
▼
focus()
Direct DOM manipulation should be kept limited; prefer Angular bindings and component APIs when they can express the same behavior.
What is viewChildren()?
viewChild() gets one matching result.
viewChild(...)
viewChildren() gets multiple matching results.
viewChildren(...)
For example:
<app-product></app-product>
<app-product></app-product>
<app-product></app-product>
You can query all three Product components.
Full viewChildren() Example
product.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-product',
template: `
<p>{{ selected ? 'Selected' : 'Not Selected' }}</p>
`
})
export class Product {
selected = false;
select() {
this.selected = true;
}
}
app.ts
import {
Component,
viewChildren
} from '@angular/core';
import { Product } from './product/product';
@Component({
selector: 'app-root',
imports: [Product],
templateUrl: './app.html'
})
export class App {
products = viewChildren(Product);
selectAll() {
this.products().forEach(product => {
product.select();
});
}
}
app.html
<app-product></app-product>
<app-product></app-product>
<app-product></app-product>
<button (click)="selectAll()">
Select All
</button>
viewChild() vs viewChildren()
viewChild() |
viewChildren() |
|---|---|
| Finds one result | Finds multiple results |
| Child component | Multiple child components |
| Element/directive/component | Multiple elements/directives/components |
| Returns a query signal | Returns a query signal containing results |
Easy way to remember:
viewChild()
│
▼
ONE
viewChildren()
│
▼
MANY
View Query vs Content Query
This distinction is useful for interviews.
A view query searches the component's own template.
Component
│
└── Own Template
│
└── viewChild()
Angular also has content queries such as:
contentChild()
contentChildren()
These query content projected into the component, rather than elements created directly in its own template.
viewChild()
│
└── My own view/template
contentChild()
│
└── Projected content
This connects directly to the ng-content concept you just learned.
Advantages of View Queries
Direct Child Access
A parent can directly access a child component when imperative control is genuinely needed.
DOM Element Access
Useful for operations such as:
focus()
scroll()
measurements
Third-Party Components
Useful when libraries expose methods that must be called directly.
Multiple Children
viewChildren() can query multiple child instances.
Reactive Modern API
Modern query functions such as viewChild() return signals, so query results work naturally with Angular's reactive APIs.
Disadvantages
Increased Coupling
Calling:
this.counter()?.reset();
means the parent knows details about the child.
That creates stronger coupling than normal input/output communication.
Can Be Overused
You should not use ViewChild simply because you want to pass ordinary data.
For normal communication:
Parent → Child
prefer:
input()
For:
Child → Parent
prefer:
output()
Direct DOM Access
Using:
ElementRef.nativeElement
too frequently can bypass Angular's normal declarative approach and make components harder to maintain.
input() vs output() vs viewChild()
This is important.
| Requirement | Use |
|---|---|
| Parent sends data to child | input() |
| Child sends event to parent | output() |
| Parent directly accesses child | viewChild() |
| Parent accesses multiple children | viewChildren() |
| Access projected child/content | contentChild() / contentChildren() |
Think of it as:
PARENT
│
│ input()
▼
CHILD
│
│ output()
▼
PARENT
Parent ── viewChild() ──► Direct Child Reference
Key Points
ng-content
- Used for content projection.
- Parent provides content.
- Child determines where it appears.
- Basic syntax:
<ng-content></ng-content>
- Multiple slots use:
<ng-content select="[header]"></ng-content>
View Queries
- Used to query items in a component's own view.
- Modern APIs:
viewChild()
viewChildren()
- Traditional APIs:
@ViewChild()
@ViewChildren()
viewChild()→ one result.viewChildren()→ multiple results.- Can query components, directives, and template references.
- Prefer
input()/output()for normal component communication; use view queries when direct access is actually required.
Interview Questions & Answers
Q1. What is ng-content?
ng-content is an Angular placeholder used for content projection. It specifies where content supplied by a parent should be rendered inside a child component.
Q2. What is a view query?
A view query allows a component to retrieve references to elements, directives, or child components from its own template.
Q3. What is ViewChild?
ViewChild retrieves a reference to a single matching element, directive, or child component from the component's view.
Modern Angular provides:
viewChild()
while existing applications commonly use:
@ViewChild()
Q4. What is the difference between viewChild() and viewChildren()?
viewChild() retrieves one matching result, while viewChildren() retrieves all matching results.
Q5. What is the difference between input() and viewChild()?
input() is used to pass data from parent to child.
product = input<Product>();
viewChild() gives the parent direct access to a child component or view element.
counter = viewChild(Counter);
Q6. Can ViewChild access an HTML element?
Yes. A template reference variable can be queried using viewChild() and accessed through ElementRef when direct DOM access is necessary.
Q7. What is the difference between a view query and a content query?
A view query searches the component's own template:
viewChild()
viewChildren()
A content query searches content projected into the component:
contentChild()
contentChildren()
Q8. Should ViewChild be used for normal parent-child communication?
Usually no. Prefer:
Parent → Child = input()
Child → Parent = output()
Use viewChild() when the parent genuinely needs a direct reference to a child component, directive, or view element.
Interview-Ready Answer
ng-contentis used for content projection and defines where parent-provided content appears inside a child component. View queries such asviewChild()andviewChildren()allow a component to obtain references to components, directives, or elements in its own template. Modern Angular provides signal-based query functions, while existing applications commonly use the decorator-based@ViewChild()and@ViewChildren()APIs.
@ViewChildren() – One More Simple Example
Here is a practical example: a parent has multiple child Product components, and clicking “Apply Discount to All” calls a method on every child.
Full Code
product.ts — Child Component
import { Component, input } from '@angular/core';
@Component({
selector: 'app-product',
templateUrl: './product.html'
})
export class Product {
name = input.required<string>();
price = input.required<number>();
discountPrice = 0;
applyDiscount() {
this.discountPrice = this.price() * 0.9;
}
}
product.html
<div>
<h3>{{ name() }}</h3>
<p>Original Price: ₹{{ price() }}</p>
@if (discountPrice > 0) {
<p>Discount Price: ₹{{ discountPrice }}</p>
}
</div>
app.ts — Parent Component
import {
Component,
QueryList,
ViewChildren
} from '@angular/core';
import { Product } from './product/product';
@Component({
selector: 'app-root',
imports: [Product],
templateUrl: './app.html'
})
export class App {
@ViewChildren(Product)
products!: QueryList<Product>;
applyDiscountToAll() {
this.products.forEach(product => {
product.applyDiscount();
});
}
}
app.html
<h1>Product List</h1>
<app-product
name="Laptop"
[price]="50000">
</app-product>
<app-product
name="Mobile"
[price]="30000">
</app-product>
<app-product
name="Tablet"
[price]="20000">
</app-product>
<button (click)="applyDiscountToAll()">
Apply 10% Discount to All
</button>
Explanation
The parent contains three instances of:
<app-product>
So conceptually:
App
│
├── Product → Laptop
├── Product → Mobile
└── Product → Tablet
We want the parent to access all three child component instances.
That's where @ViewChildren() is useful.
1. Query all Product children
@ViewChildren(Product)
products!: QueryList<Product>;
Angular searches the parent's view for all Product components.
The result is stored in a:
QueryList<Product>
Conceptually:
@ViewChildren(Product)
│
▼
QueryList<Product>
│
├── Product → Laptop
├── Product → Mobile
└── Product → Tablet
2. Loop through all children
When the button is clicked:
<button (click)="applyDiscountToAll()">
the parent executes:
applyDiscountToAll() {
this.products.forEach(product => {
product.applyDiscount();
});
}
this.products contains all three child component instances.
Therefore:
this.products
│
├── Laptop → applyDiscount()
├── Mobile → applyDiscount()
└── Tablet → applyDiscount()
3. Parent calls the child's method
Each child contains:
applyDiscount() {
this.discountPrice = this.price() * 0.9;
}
So the result becomes:
Laptop
₹50,000 → ₹45,000
Mobile
₹30,000 → ₹27,000
Tablet
₹20,000 → ₹18,000
What is QueryList?
With traditional @ViewChildren(), Angular returns a QueryList, which represents a collection of matching children.
@ViewChildren(Product)
products!: QueryList<Product>;
You can work with it similarly to a collection.
For example:
this.products.forEach(product => {
console.log(product);
});
You can also check:
console.log(this.products.length);
In our example:
3
@ViewChild() vs @ViewChildren()
The difference becomes very clear with this example.
One child
@ViewChild(Product)
product!: Product;
Parent
│
└── Product
Multiple children
@ViewChildren(Product)
products!: QueryList<Product>;
Parent
│
├── Product
├── Product
└── Product
So remember:
@ViewChild
↓
ONE
@ViewChildren
↓
MANY
Interview-Ready Answer
@ViewChildren()is used when a component needs references to multiple matching child components, directives, or elements in its view. It returns aQueryList. For example, if a parent contains multipleProductcomponents,@ViewChildren(Product)can retrieve all of them, allowing the parent to iterate through the children and call methods such asapplyDiscount()on each instance.