The minimal code to produce the issue can be seen at here.
Objective: Showing a list of persons based on the selected gender. If All
is selected then all persons are shown.
Issue:
-
When the page is first loaded,
All
is selected by default. All persons are shown. It works as expected. -
Selecting
male
shows a list of male persons. It works as expected. -
But if I reselect
All
, no person is shown. It is the unexpected result.
Code Snippet:
<div>
<select [(ngModel)]="selectedFilter">
<option [value]="undefined">All</option>
<option *ngFor="let gender of ['male','female']" [value]="gender">
{{gender}}
</option>
</select>
<ul>
<li *ngFor="let person of peopleByGender">
{{person.name}}
</li>
</ul>
</div>
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
selectedFilter?: "male" | "female";
people: { name: string; gender: "male" | "female"; }[] = [
{ name: "Anton", gender: "male" },
{ name: "Bob", gender: "male" },
{ name: "Cindy", gender: "female" }
];
get peopleByGender(): readonly { name: string; gender: "male" | "female"; }[] {
return this.people
.filter(x => this.selectedFilter == undefined || x.gender == this.selectedFilter);
}
}
Question
What causes this issue? And how to solve it?
Note: Using type
or interface
are intentionally ignored to focus on the main issue.
Logging
get peopleByGender(): readonly { name: string; gender: "male" | "female"; }[]
{
console.log(
`Selected Filter: ${this.selectedFilter}.
Is it undefined? ${this.selectedFilter == undefined}`
);
// ... others
}
>Solution :
<option>
values must be convertible to strings, because behind the scenes they’re getting represented as HTML DOM.
So when a user selects the "All" option, they’re not changing the value to undefined
: they’re changing it to "undefined"
.
Instead of trying to use undefined
, use a special string value. For example, you could use "All", or you could just use an empty string:
<option value="">All</option>
export class AppComponent {
selectedFilter: "male" | "female" | "" = "";
people: { name: string; gender: "male" | "female"; }[] = [
{ name: "Anton", gender: "male" },
{ name: "Bob", gender: "male" },
{ name: "Cindy", gender: "female" }
];
get peopleByGender(): readonly { name: string; gender: "male" | "female" | ""; }[] {
return this.people
.filter(x => this.selectedFilter === "" || x.gender == this.selectedFilter);
}