Combobox
JavaScript off: what the HTML and CSS do on their own.
Usage
@* A list people filter by typing. *@
<Combobox @bind-Value="Model!.Country" Items="Countries.Keys"
ItemText="code => Countries.GetValueOrDefault(code!, code!)"
Placeholder="Search countries" />
@* A list searched as you type, by your code. *@
<Combobox @bind-Value="Model!.OwnerId" Search="FindPeople"
ItemText="id => People.GetValueOrDefault(id!.Value)?.Name ?? id.ToString()!" />Typing to filter
Use Items and ItemText for a long list people find by typing: what's typed filters the options, ignoring case and accents.
Preview
Code
<Form Model="Model" FormName="combobox-filtering" MaxWidth="24.Rem" Grow>
<Combobox @bind-Value="Model!.Country" Items="Countries.Keys"
ItemText="code => Countries.GetValueOrDefault(code!, code!)"
Placeholder="Search countries" />
</Form>
{
[SupplyParameterFromForm(FormName = "combobox-filtering")]
private Shipping? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
public sealed class Shipping
{
public string? Country { get; set; }
}
}Off: A plain dropdown of every country, with no typing to filter.
Typing an option's whole text and leaving the field chooses it, so keep each ItemText unique. Bind an enum and its values are the options, named by their [Display(Name)], with no Items. For a short list, a Select is simpler; for several choices, use a MultiSelect.
Searching a long list
Use Search for a list too long to send whole: it gets what's typed and returns the first matching values, of the bound type, and ItemTemplate adds markup to each option, such as an avatar.
Preview
Code
System.ComponentModel.DataAnnotations
<Form Model="Model" FormName="combobox-server-search" MaxWidth="24.Rem" Grow>
<Combobox @bind-Value="Model!.OwnerId" Search="FindPeople"
ItemText="id => People.GetValueOrDefault(id!.Value)?.Name ?? id.ToString()!"
Placeholder="Search people">
<ItemTemplate Context="id">
<Avatar Name="(People[id!.Value].Name)" Size="AvatarSize.Xs" Decorative />
<span>(People[id!.Value].Name) <span class="ib-option-hint">(People[id!.Value].Team)</span></span>
</ItemTemplate>
</Combobox>
</Form>
{
[SupplyParameterFromForm(FormName = "combobox-server-search")]
private Issue? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
// Called with "" for the first list, then with what's typed on interactive pages.
private static async Task<IEnumerable<int?>> FindPeople(string text, CancellationToken token)
{
await Task.Delay(text.Length == 0 ? 0 : 300, token); // a database query
return People.Where(p => p.Value.Name.Contains(text, StringComparison.OrdinalIgnoreCase))
.Take(8)
.Select(p => (int?)p.Key);
}
public sealed class Issue
{
[Display(Name = "Owner")]
public int? OwnerId { get; set; }
}
}Off: A plain dropdown of what Search returns for an empty string: the first eight people.
Search is called with "" to fill the list on every page, and on interactive pages again as someone types, Debounce milliseconds after the last key (250 by default). On a static page the browser filters that first list, so return what people are most likely to want, or put the page in an interactive render mode. Search runs where the component does: on the server, or in the browser on WebAssembly, where it calls your API.
The field shows ItemText for the chosen value even when the results don't include it, so ItemText gets any value that's bound or posted: look it up without throwing, as the examples do, and check on the server that a posted value is allowed.
Required, with a placeholder
Use a nullable property with [Required] and a Placeholder: nothing is chosen until someone picks an option, and the form doesn't post without one.
Preview
Code
System.ComponentModel.DataAnnotations
<Form Model="Model" FormName="combobox-required" OnValidSubmit="Save" MaxWidth="24.Rem" Grow>
<Combobox @bind-Value="Model!.Country" Items="Countries.Keys"
ItemText="code => Countries.GetValueOrDefault(code!, code!)"
Placeholder="Search countries" />
<Row><Button Submit>Continue</Button></Row>
if (_shippingTo is not null)
{
<Alert Status="Status.Success">Shipping to _shippingTo.</Alert>
}
</Form>
{
[SupplyParameterFromForm(FormName = "combobox-required")]
private Shipping? Model { get; set; }
private string? _shippingTo;
protected override void OnInitialized() => Model ??= new();
// A posted value can be anything: check it's one of the options.
private void Save() => _shippingTo = Countries.GetValueOrDefault(Model!.Country!);
public sealed class Shipping
{
[Required(ErrorMessage = "Choose a country to ship to"), Display(Name = "Ship to")]
public string? Country { get; set; }
}
}Off: A plain dropdown: the browser stops the post until a country is chosen, in its own words.
Bind a nullable type: an int or an enum always has a value (0 or the first member), so the placeholder never shows and emptying the field is an error.
Sizes, read-only and disabled
Use Size to line up with buttons and other fields, ReadOnly for a value that posts but can't change, and Disabled for one that doesn't post.
Preview
Code
<Form Model="Model" FormName="combobox-states" MaxWidth="24.Rem" Grow>
<Combobox @bind-Value="Model!.Small" Items="Timezones.Keys" ItemText="Name"
Label="Small" Size="ControlSize.Sm" />
<Combobox @bind-Value="Model!.Large" Items="Timezones.Keys" ItemText="Name"
Label="Large" Size="ControlSize.Lg" />
<Combobox @bind-Value="Model!.Office" Items="Timezones.Keys" ItemText="Name"
Label="Office time zone" ReadOnly Hint="Read-only: posted, but can't be changed." />
<Combobox @bind-Value="Model!.Backup" Items="Timezones.Keys" ItemText="Name"
Label="Backup time zone" Disabled Hint="Disabled: not posted." />
</Form>
{
[SupplyParameterFromForm(FormName = "combobox-states")]
private Settings? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
private static string Name(string? id) => Timezones.GetValueOrDefault(id!, id!);
public sealed class Settings
{
public string? Small { get; set; } = "Europe/London";
public string? Large { get; set; } = "Asia/Tokyo";
public string? Office { get; set; } = "Europe/Paris";
public string? Backup { get; set; } = "America/New_York";
}
}HTML has no read-only select, so a read-only combobox shows its choice in a read-only text field and posts it from a hidden input.
Its own words
Use EmptyText for "No matches", SearchingText for "Searching…" and ToggleLabel for the name of the button that opens the list, or set them for the whole app as in Text and other languages. Type "zzz" in the first example to see "No matches".
Mode support
| Feature | Off | Static | Live |
|---|---|---|---|
| Choose an option, posted with the form | Yes | Yes | Yes |
| Type to filter, ignoring case and accents | No | Yes | Yes |
| Markup in each option (ItemTemplate) | No | Yes | Yes |
| Search fills the list | Yes | Yes | Yes |
| Search as you type | No | No | Yes |
Off: JavaScript disabled. Static: a static SSR page with IzzyBlazor's script. Live: an interactive page (Server, WebAssembly or Auto).
Accessibility
- With script, the input is a
comboboxwith alistboxof options: focus stays in the input, and the highlighted option is itsaria-activedescendant. - Down and Up open the list and move through it, Page Down and Page Up move ten, Enter chooses, and Alt+Up chooses and closes.
- Escape closes the list and puts the chosen text back, without closing a dialog around it; Tab closes it too.
- Leaving the field takes an option whose text was typed in full, and clears the value when the text was emptied.
- When nothing matches, "No matches" (
EmptyText) is announced politely, as is "Searching…" while a server search runs. - The label names the input and the list; the chevron button is named "Show options" (
ToggleLabel) and is left out of the Tab order. - With JavaScript off, it's a native
select, with its own keys and announcements.
Parameters
| Prop | Type | Default |
|---|---|---|
ReadOnlyMakes the field read-only: the chosen text shows in a read-only text field, and the value still posts. | bool | false |
ItemsThe options. | IEnumerable<TValue>? | |
SearchFinds options for the typed text, for lists too long to send whole. | Func<string, CancellationToken, Task<IEnumerable<TValue>>>? | |
DebounceMilliseconds to wait after the last keystroke before calling Search. | int | 250 |
ItemTextThe text for each option, also what's typed to find it and what the field shows once chosen. | Func<TValue, string>? | computed |
ItemTemplateMarkup for each option in the list, such as an icon or avatar. | RenderFragment<TValue>? | |
PlaceholderText in the empty field, such as "Search countries". | string? | |
EmptyTextShown when nothing matches. | string? | |
SearchingTextShown while Search runs. | string? | |
ToggleLabelThe accessible name of the button that opens the list. | string? | |
@bind-ValueThe value, bound with @bind-Value. | TValue | |
DisplayNameThe field's name in validation messages from the input itself, such as a value that can't be parsed. | string? |
Plus 26 field props.
Full APISee also
- Select: a native select, themed where the browser allows.
- MultiSelect: several choices as chips you can type to filter.
- TextField: a labeled text input with add-ons and a password reveal.