Free tools Windows power users keep installed
One-click scans. No signup required.
An empty-looking Angular dropdown entry usually means the control’s current model value does not match any option that has been rendered. In AngularJS, this mismatch can produce an explicit “unknown option”; a common cause is a numeric model such as 200 being compared with the string option value "200". In modern Angular, the equivalent problems are inconsistent value types, object identity mismatches, options that have not loaded yet, or an intentional value="" placeholder.
First determine which Angular you are using
“Angular” can refer to two different framework generations with different select behavior and APIs.
| Framework | Typical template APIs | Relevant behavior |
|---|---|---|
| AngularJS 1.x | ng-model, ng-options |
A select can display an unknown option when the model value matches none of the available options. AngularJS support ended in January 2022. |
| Modern Angular | [(ngModel)], reactive forms, [ngValue] |
The control tracks rendered options and their values. A nonmatching value, an object-identity mismatch, or an option list that is not ready can leave the select with no matching selection. |
Check package.json and the template syntax before applying a fix. Advice for ng-model and ng-options should not be copied directly into a modern Angular component.
What the empty entry actually means
The blank row is not normally a new record in your data. It is the select’s way of representing a value for which it cannot find a rendered option. The visible label can be misleading: two options may both display 200, while one model contains the number 200 and the other contains the string "200".
#1 Best Overall
Before changing the template, inspect both sides of the comparison:
- The model’s value and JavaScript type.
- Every rendered option’s actual value, not just its text.
- Whether options are present yet when the model is initialized.
- Whether the option is an object and, if so, whether it is the same object instance as the model value.
- Whether the template or a component library deliberately added an empty option.
AngularJS: fix strict type mismatches
AngularJS 1.4 and later use strict model-to-option matching. A number and a string that look identical are different values:
// Model value: number
$scope.categoryId = 200;
// HTML option value: string
<option value="200">Hardware</option>
Because 200 !== "200", AngularJS cannot select the option and may insert an unknown option.
Rank #2
Keep the model as a string
Use this when the form naturally submits text and no numeric operations are required:
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware match$scope.categoryId = "200";
Bind a numeric value with ng-value
Use ng-value when the model must remain numeric:
<select ng-model="categoryId">
<option ng-value="100">Accessories</option>
<option ng-value="200">Hardware</option>
</select>
Generate options with ng-options
For a data-backed list, ng-options keeps the option expression and model representation aligned:
<select ng-model="categoryId"
ng-options="category.id as category.name for category in categories">
<option value="">Choose a category</option>
</select>
If your application converts values at a boundary, make the conversion deliberate and consistent. Do not rely on the displayed label to imply a numeric type.
Rank #3
Modern Angular: bind the intended value type
Use value for simple string values
Plain HTML option values are strings. Keep the model string-valued when that is the intended domain:
<select [(ngModel)]="categoryId">
<option value="100">Accessories</option>
<option value="200">Hardware</option>
</select>
categoryId = '200';
Use [ngValue] for numbers or objects
Angular’s ngValue binding preserves non-string values, including numbers and objects:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →<select [(ngModel)]="categoryId">
<option [ngValue]="100">Accessories</option>
<option [ngValue]="200">Hardware</option>
</select>
categoryId = 200;
For object options, bind the object itself:
<select [(ngModel)]="selectedCategory">
<option *ngFor="let category of categories" [ngValue]="category">
{{ category.name }}
</option>
</select>
Object options: use compareWith when identity changes
Angular compares object options by identity by default. An object received from an API and a separately constructed object with the same ID are still different references:
Rank #4
selectedCategory = { id: 200, name: 'Hardware' };
categories = [
{ id: 200, name: 'Hardware' }
];
Those objects have equal properties but are not the same instance, so the select may not mark the option selected. Compare a stable key instead:
<select [(ngModel)]="selectedCategory" [compareWith]="compareCategory">
<option *ngFor="let category of categories" [ngValue]="category">
{{ category.name }}
</option>
</select>
compareCategory = (a: Category | null, b: Category | null): boolean =>
a?.id === b?.id;
Make the comparison null-safe and use a key that is unique and stable for the option set.
Asynchronous options and initialization order
A model is often assigned before an HTTP request finishes. During that interval, the select has no matching option. When the list arrives, Angular’s option directive notifies the select as dynamic options change, but your code still needs to ensure that the eventual model is valid.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsRequired selection
- Load the option list.
- Check whether the existing model value appears in that list.
- Set the model to a valid option, or leave it unset until a user chooses one.
this.categoriesService.getAll().subscribe(categories => {
this.categories = categories;
const exists = categories.some(c => c.id === this.categoryId);
if (!exists) {
this.categoryId = categories[0]?.id ?? null;
}
});
Adapt the fallback to your product rules; selecting the first item automatically is not appropriate when the user must make an explicit choice.
Optional selection
Represent “nothing selected” intentionally instead of treating a mismatch as a valid state. Use a clear prompt:
<select [(ngModel)]="categoryId">
<option value="" disabled>Choose a category</option>
<option *ngFor="let category of categories" [value]="category.id">
{{ category.name }}
</option>
</select>
Here the model should use the same representation as the option values—for example, an empty string for the prompt and strings for IDs. If IDs are numbers, use [ngValue] for both the prompt’s intended null state and the numeric options, or normalize the model at the form boundary.
Check for an authored empty option
Search the template and any select component for:
<option value="">- A disabled “Choose…” or “Select…” prompt
- A library setting that adds a placeholder
- Conditional rendering that creates an option before the data list
An explicit empty-string option is often correct: it represents a null or not-selected state rather than framework-generated data. Remove it only when the product requires a valid selection and the model is initialized accordingly.
A practical debugging checklist
- Identify AngularJS versus modern Angular from the APIs and package versions.
- Log the model value with its type, such as
typeof categoryId. - Inspect the rendered option values in browser developer tools.
- For primitives, make the model and option types identical.
- For objects, use
[ngValue]and addcompareWithwhen object instances are recreated. - Verify that asynchronous data has loaded before assuming a value is invalid.
- Decide explicitly whether an empty state is allowed.
- Search for a literal empty option or component-library placeholder.
Common fixes that do not solve the cause
- Changing the option’s visible text: labels do not change the underlying value.
- Calling
parseIntin the template without normalizing every option and model update: this can create inconsistent types elsewhere. - Adding a second placeholder: it hides the mismatch and can create duplicate empty entries.
- Using
compareWithfor primitive type errors: it is intended for object comparison, not for making a number equal to a string.
The Bottom Line
Fix the mismatch rather than hiding the blank row: align primitive types, use ngValue for numbers or objects, compare object IDs with compareWith, wait for asynchronous options, and keep an empty option only when “no selection” is a deliberate product state.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

