Question
In Angular 2 with TypeScript, how can you access the newly selected value from a <select> element?
In the following example, the onChange() handler is called when the selection changes, but this.selectedDevice appears to contain the previous value instead of the newly selected one:
<select [(ngModel)]="selectedDevice" (change)="onChange($event)">
<option *ngFor="let i of devices">{{ i }}</option>
</select>
onChange($event: Event) {
console.log(this.selectedDevice);
// I want to work with the newly selected device here,
// but this logs the previous selection instead.
}
How do you get the current selected value when the user chooses a new option?
Short Answer
By the end of this page, you will understand why a native (change) event can feel out of sync with Angular's two-way binding, and how to correctly get the new selected value from a <select> using ngModelChange, template references, or the event target.
Concept
In Angular forms, a <select> can be connected to a component property using [(ngModel)]. This is called two-way binding.
<select [(ngModel)]="selectedDevice">
This means:
- Angular writes the component value into the
<select> - Angular updates the component property when the user picks a new option
The confusion usually comes from mixing:
- the native DOM
(change)event - Angular's
[(ngModel)] - Angular's
(ngModelChange)event
Why this matters
When you use (change), you are listening to the browser's native event from the <select> element. That event gives you access to the DOM event object, but depending on how you read your component property, it may not yet look updated in the way you expect.
Angular also provides (ngModelChange), which is specifically designed to work with ngModel. It emits the new value directly.
The key idea
If your goal is:
- "Give me the newly selected value"
then the most Angular-friendly approach is:
<select [(ngModel)]="selectedDevice" (ngModelChange)="onChange($event)">
Here, $event is the new selected value itself.
This matters in real projects because form controls are everywhere:
- dropdown filters
- settings pages
- product selectors
- status updates
- admin dashboards
Understanding the difference between DOM events and Angular form events helps you avoid stale values and write clearer code.
Mental Model
Think of a <select> as a form control with two messengers:
- The browser messenger says: "The user changed something in the DOM."
- The Angular messenger says: "The model value is now this new value."
If you listen to the browser messenger with (change), you may need to manually read the value from the event target.
If you listen to the Angular messenger with (ngModelChange), Angular hands you the updated value directly.
So:
(change)= "something changed in the element"(ngModelChange)= "here is the new model value"
That is why (ngModelChange) is often easier when using [(ngModel)].
Syntax and Examples
Recommended syntax: ngModelChange
<select [(ngModel)]="selectedDevice" (ngModelChange)="onChange($event)">
<option *ngFor="let device of devices" [value]="device">{{ device }}</option>
</select>
selectedDevice = '';
devices = ['Phone', 'Tablet', 'Laptop'];
onChange(newDevice: string) {
console.log('New selection:', newDevice);
console.log('Component property:', this.selectedDevice);
}
Why this works
[(ngModel)]="selectedDevice"keeps the component property in sync(ngModelChange)passes the new selected value as$eventnewDeviceis the current selection
Using the native change event
If you want to keep (change), read the selected value from the DOM event:
<select [(ngModel)]="selectedDevice" (change)="onChange($event)">
<option *ngFor="let device of devices" [value]="device">{{ device }}</option>
</select>
onChange(event: Event) {
const value = (event.target as HTMLSelectElement).value;
console.log('New selection:', value);
}
Using a template reference variable
Another simple option is to read the select element directly:
<select #deviceSelect [(ngModel)]="selectedDevice" (change)="onChange(deviceSelect.value)">
<option *ngFor="let device of devices" [value]="device">{{ device }}</option>
</select>
onChange(value: string) {
console.log('New selection:', value);
}
Important note about *ngFor
Modern Angular syntax uses:
*ngFor="let item of items"
Older examples may show:
*ngFor="#item of items"
The let version is the standard syntax.
Step by Step Execution
Consider this example:
<select [(ngModel)]="selectedDevice" (ngModelChange)="onChange($event)">
<option *ngFor="let device of devices" [value]="device">{{ device }}</option>
</select>
devices = ['Phone', 'Tablet', 'Laptop'];
selectedDevice = 'Phone';
onChange(newDevice: string) {
console.log('Selected:', newDevice);
}
What happens step by step
selectedDevicestarts as'Phone'- Angular shows
'Phone'as the selected option - The user changes the selection to
'Laptop' - Angular detects the form control update
ngModelupdatesselectedDeviceto'Laptop'(ngModelChange)emits'Laptop'onChange('Laptop')runs- The console prints:
Selected: Laptop
Compare that with (change)
If you use:
(change)="onChange($event)"
then onChange() receives a browser event object, not the new model value directly.
You must extract the value yourself:
onChange(event: Event) {
const value = (event.target as HTMLSelectElement).value;
console.log(value);
}
Real World Use Cases
This pattern is useful anywhere a dropdown controls app behavior.
Common examples
- Filtering data
- Select a category and reload a product list
- Choosing a device or environment
- Update a settings panel based on the selected option
- Sorting results
- Change from
NewesttoPrice: Low to High
- Change from
- Admin dashboards
- Select a user role, status, or region
- Forms
- Choose a country, payment method, or shipping option
Example: filter a list
<select [(ngModel)]="selectedCategory" (ngModelChange)="filterProducts($event)">
<option *ngFor="let category of categories" [value]="category">{{ category }}</option>
</select>
filterProducts(category: string) {
console.log('Filter by:', category);
}
The selected value can trigger API calls, local filtering, or UI updates.
Real Codebase Usage
In real Angular projects, developers usually prefer patterns that make state changes explicit and easy to read.
Common patterns
1. Use ngModelChange when you need the new value immediately
<select [(ngModel)]="status" (ngModelChange)="updateStatus($event)">
This is clear and works well for simple forms.
2. Use guard clauses
If a value is empty or invalid, exit early.
updateStatus(status: string) {
if (!status) {
return;
}
console.log('Updating status to', status);
}
3. Validate before acting
updateDevice(device: string) {
if (!this.devices.includes(device)) {
console.error('Unknown device');
return;
}
this.selectedDevice = device;
}
4. Trigger related updates
A selection often affects more than one thing.
onDeviceChange(device: string) {
this.selectedDevice = device;
this.loadDeviceSettings(device);
this.clearMessages();
}
5. Keep templates simple
Pass the value to the component method and do the logic in TypeScript.
Good:
<select [(ngModel)]="selectedDevice" (ngModelChange)="onDeviceChange($event)">
Avoid putting too much logic in the template.
Why teams prefer this
- easier to test
- easier to debug
- less confusion about event timing
- better separation between template and logic
Common Mistakes
1. Expecting $event from (change) to be the selected value
Broken assumption:
<select (change)="onChange($event)"></select>
onChange(value: string) {
console.log(value); // This is not the selected string value
}
$event here is a DOM event object, not the selected option string.
Fix:
onChange(event: Event) {
const value = (event.target as HTMLSelectElement).value;
console.log(value);
}
Or use:
(ngModelChange)="onChange($event)"
2. Reading the component property at the wrong time
onChange(event: Event) {
console.log(this.selectedDevice);
}
This can be confusing when you expect it to always reflect the current DOM change immediately.
Safer approach:
<select [(ngModel)]="selectedDevice" (ngModelChange)="onChange($event)">
3. Omitting [value] on complex options
For simple strings, interpolation may work, but explicitly binding values is clearer.
Better:
<option *ngFor="let device of devices" [value]="device">{{ device }}</option>
4. Using outdated *ngFor syntax
Older syntax:
<option *ngFor="#i of devices">{{ i }}</option>
Current syntax:
<option *ngFor="let i of devices">{{ i }}</option>
5. Confusing change with ngModelChange
They are related, but not the same:
changeis a native DOM eventngModelChangeis an Angular form event that emits the new model value
Comparisons
| Approach | What it gives you | Best use case | Example |
|---|---|---|---|
(change) | DOM event object | When you need low-level event details | (change)="onChange($event)" |
(ngModelChange) | New bound value | When using [(ngModel)] and you want the updated selection | (ngModelChange)="onChange($event)" |
| Template reference variable | Direct element value | Simple templates where you want explicit control | (change)="onChange(selectRef.value)" |
change vs ngModelChange
| Feature | change | ngModelChange |
|---|---|---|
| Source | Browser DOM event | Angular forms binding |
$event contains | Event object | New model value |
| Best for | Native event handling | Reading updated form values |
Works with [(ngModel)] | Yes | Yes |
| Beginner-friendly for forms | Less | More |
Recommendation
If your goal is simply to get the newly selected value from a dropdown bound with ngModel, prefer ngModelChange.
Cheat Sheet
<!-- Best choice when using ngModel -->
<select [(ngModel)]="selectedValue" (ngModelChange)="onChange($event)">
<option *ngFor="let item of items" [value]="item">{{ item }}</option>
</select>
onChange(value: string) {
console.log(value);
}
Native change event
<select (change)="onChange($event)">
onChange(event: Event) {
const value = (event.target as HTMLSelectElement).value;
}
Template reference variable
<select #s (change)="onChange(s.value)">
Rules to remember
[(ngModel)]= two-way binding(change)= native DOM event(ngModelChange)= new Angular model value- Use
*ngFor="let item of items" - Use
[value]="item"for clarity
Common edge case
If options are objects instead of strings, use Angular bindings carefully, often with [ngValue] instead of [value].
FAQ
Why does Angular (change) give me the old value?
Because (change) is a native DOM event, not Angular's model-change event. If you need the new bound value directly, use (ngModelChange).
What is the difference between change and ngModelChange in Angular?
change comes from the browser and gives an event object. ngModelChange comes from Angular forms and gives the new model value.
How do I get the selected option value from a select in Angular?
The simplest way is:
<select [(ngModel)]="selectedValue" (ngModelChange)="onChange($event)">
Can I still use (change) with ngModel?
Yes. You just need to read the value from the event target:
const value = (event.target as HTMLSelectElement).value;
Should I use [value] in Angular option elements?
Yes, it is clearer and more explicit, especially when generating options with *ngFor.
What if my select options are objects, not strings?
Use [ngValue] when binding full objects. [value] converts values to strings.
Is *ngFor="#item of items" still correct?
That is older syntax. In current Angular code, use *ngFor="let item of items".
Mini Project
Description
Build a small device selector component that lets a user choose a device from a dropdown and immediately displays a message showing the current selection. This demonstrates how to capture the newly selected value correctly in Angular using ngModelChange.
Goal
Create a dropdown that updates the component state and displays the newly selected device as soon as the user changes it.
Requirements
- Create a list of device names in the component
- Bind the select element to a component property using
ngModel - Call a method whenever the selection changes
- Show the currently selected device below the dropdown
- Use the newly selected value inside the event handler
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.