Question
In an Angular component, @ViewChild('nameInput') and @ViewChild('amountInput') produce this TypeScript error:
TS2554: Expected 2 arguments, but got 1.
An argument for 'opts' was not provided.
How should @ViewChild be declared so Angular can access these template reference variables?
@ViewChild('nameInput') nameInputRef: ElementRef;
@ViewChild('amountInput') amountInputRef: ElementRef;
Short Answer
You will learn how Angular's @ViewChild decorator finds elements or child components in a template, why some Angular versions require an options object, and how { static: true } and { static: false } affect when the query is available.
Concept
@ViewChild is an Angular property decorator used to get a reference to something in a component's own template. That "something" can be:
- A native DOM element marked with a template reference such as
#nameInput - A child component
- A directive
- A
TemplateRef
For example, if the template contains:
<input #nameInput type="text">
Angular can assign that input element to a class property:
@ViewChild('nameInput', { static: false })
nameInputRef!: ElementRef<HTMLInputElement>;
The second argument is the query options object. In Angular versions whose ViewChild type definition requires it, omitting that object causes TypeScript error TS2554.
The most important option is static:
static: falsemeans the query is resolved after Angular has rendered and checked the view. This is the usual choice for elements that might be inside , , or other dynamic template blocks.
Mental Model
Think of a component template as a room and @ViewChild as asking Angular for the location of a particular object in that room.
#nameInputis a label attached to an object.@ViewChild('nameInput', ...)is your request to Angular: “Find the object with this label.”{ static: false }means: “Give me the location once the room has been set up and rendered.”{ static: true }means: “This object will definitely already be in the room, so find it immediately.”
If the object is behind a conditional door such as *ngIf, it may not exist at first. In that case, static: false is the safer choice.
Syntax and Examples
Use a template reference variable in HTML, then query it from the component class.
<input #nameInput type="text" placeholder="Ingredient name">
<input #amountInput type="number" placeholder="Amount">
<button type="button" (click)="onAddItem()">Add</button>
import { Component, ElementRef, EventEmitter, Output, ViewChild } from '@angular/core';
import { Ingredient } from 'src/app/shared/ingredient.model';
@Component({
selector: 'app-shopping-edit',
templateUrl: './shopping-edit.component.html',
styleUrls: ['./shopping-edit.component.css']
})
export {
(, { : })
nameInputRef!: <>;
(, { : })
amountInputRef!: <>;
() ingredientAdded = <>();
(): {
ingName = ...;
ingAmount = (...);
..( (ingName, ingAmount));
}
}
Step by Step Execution
Consider this template and component method:
<input #nameInput value="Apples">
<button type="button" (click)="onAddItem()">Add</button>
@ViewChild('nameInput', { static: false })
nameInputRef!: ElementRef<HTMLInputElement>;
onAddItem(): void {
const name = this.nameInputRef.nativeElement.value;
console.log(name);
}
Execution flow:
- Angular creates
ShoppingEditComponent. - Angular renders its template, including the
<input #nameInput>element. - Because the query uses
static: false, Angular resolvesnameInputRefafter the view is initialized.
Real World Use Cases
@ViewChild is useful when Angular data binding alone is not enough or when a direct element or component API is needed.
- Focus a form field: Put the cursor in a search box after a dialog opens.
- Read or set native element state: Access a video element to call
play()orpause(). - Call a child component method: Ask a reusable modal, editor, or chart component to reset or refresh itself.
- Integrate browser or third-party APIs: Pass a canvas element to a charting library after the view exists.
- Measure layout: Read element dimensions after rendering for positioning logic.
Example: focusing an input after the view is available:
@ViewChild('searchBox', { static: false })
searchBox!: ElementRef<HTMLInputElement>;
ngAfterViewInit(): void {
this.searchBox.nativeElement.focus();
}
Real Codebase Usage
In production Angular applications, prefer declarative binding for ordinary form values:
<input [(ngModel)]="ingredientName">
or reactive forms:
form = new FormGroup({
name: new FormControl('', { nonNullable: true })
});
Use @ViewChild when you specifically need the element, a child component instance, or a browser API.
A common pattern is querying a child component instead of querying its DOM:
@ViewChild(IngredientFormComponent, { static: false })
formComponent!: IngredientFormComponent;
save(): void {
if (!this.formComponent.isValid()) {
return;
}
this.formComponent.submit();
}
This keeps parent components dependent on a child's public methods rather than its internal HTML structure.
Common Mistakes
Omitting required query options
In Angular versions where the decorator signature requires options, this produces TS2554:
// Broken when the installed Angular typings require options
@ViewChild('nameInput')
nameInputRef!: ElementRef;
Pass an options object:
@ViewChild('nameInput', { static: false })
nameInputRef!: ElementRef<HTMLInputElement>;
Using a dynamic query in ngOnInit
A static: false query is not ready during ngOnInit.
// Broken: nameInputRef may be undefined here
ngOnInit(): void {
this.nameInputRef.nativeElement.focus();
}
Use ngAfterViewInit instead:
Comparisons
| Choice | When it is available | Best use case |
|---|---|---|
@ViewChild(..., { static: true }) | Early in the component lifecycle, including ngOnInit | The target always exists in the initial template |
@ViewChild(..., { static: false }) | After the view initializes, such as ngAfterViewInit | The target can change or is inside *ngIf / *ngFor |
| Template binding | During Angular's normal change detection | Displaying and updating ordinary UI data |
| Reactive forms | Through form controls and validators | Managing form fields, validation, and submission |
@ViewChild returns one matching item. If a template has multiple matching items, use @ViewChildren instead:
Cheat Sheet
import { AfterViewInit, ElementRef, ViewChild } from '@angular/core';
@ViewChild('inputRef', { static: false })
inputRef!: ElementRef<HTMLInputElement>;
<input #inputRef>
- The string in
@ViewChild('inputRef', ...)must match#inputRefin the template. - Pass
{ static: false }for dynamic or conditionally rendered targets. - A
static: falsequery is safe inngAfterViewInitand event handlers. - Use
{ static: true }only when the target always exists on first render. - Use
!because Angular assigns the property after construction. nativeElement.valueis a string; useNumber(...)for numeric values.- Prefer binding or Angular forms for normal form state; use
ElementReffor direct element APIs when needed.
FAQ
Why does @ViewChild say Expected 2 arguments but got 1?
Your installed Angular type definitions require the query options argument. Add an options object, usually { static: false }.
Should I use static: true or static: false with @ViewChild?
Use static: false by default, especially for elements controlled by *ngIf or *ngFor. Use static: true only if the target always exists in the initial template and you need it in ngOnInit.
Why is my @ViewChild undefined in ngOnInit?
A query with static: false is resolved after the view is initialized. Access it in ngAfterViewInit or later.
Do I need ElementRef for @ViewChild?
Only when querying a native DOM element. When querying a child component, type the property as that component class instead.
Why use instead of ?
Mini Project
Description
Build a small ingredient-entry component that reads two input elements through @ViewChild, validates the values, and emits a new ingredient object. This demonstrates how view queries work after Angular renders a template.
Goal
Create an ingredient form that emits a valid ingredient when the user clicks Add Ingredient.
Requirements
Include text and numeric inputs with template reference variables.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.