我对 Blazor 和新的 Fluent UI 相当陌生。我正在尝试根据 API 中的值创建一组分层下拉列表(一个列表中的值基于另一个列表中的用户选择),但似乎无法让占位符文本按照我的方式运行喜欢。
使用以下 Blazor 代码,我模拟了我的问题的示例。它使用
FluentCombobox
因为它有一个方便的 Placeholder
属性。问题是,当子列表发生变化时,之前选择的值会被保留。
重现步骤:
Placeholder
文本,而不是保留之前选择的值。Blazor 页面
@page "/Vehicles"
@rendermode InteractiveServer
<h3>Vehicles</h3>
<FluentCombobox Label="Make:" Placeholder="Select a make..." Items="@Makes" ValueChanged="MakeValueChanged" Disabled="@MakesEmpty" Autocomplete="ComboboxAutocomplete.List" Height="300px" />
<br/>
<FluentCombobox Label="Model:" Placeholder="Select a model..." Items="@Models" ValueChanged="ModelValueChanged" Disabled="@ModelsEmpty" Autocomplete="ComboboxAutocomplete.List" Height="300px" />
<br />
<FluentButton @onclick="OnSearchClick" Disabled="@SearchDisabled">Search</FluentButton>
@code {
public string Make { get; set; } = string.Empty;
public string Model { get; set; } = string.Empty;
private IEnumerable<string> Makes { get; set; } = [];
private IEnumerable<string> Models { get; set; } = [];
private IDictionary<string, IEnumerable<string>> MakesModels { get; set; } = new Dictionary<string, IEnumerable<string>>
{
{"Audi", new string[] { "A4", "A6", "A8" } },
{"BMW", new string[] { "3 Series", "4 Series", "6 Series" } },
{"Volvo", new string[] { "C30", "C40", "C70" } },
};
private bool MakesEmpty { get { return this.Makes.Count() == 0; } }
private bool ModelsEmpty { get { return this.Models.Count() == 0; } }
private bool SearchDisabled { get { return this.Model.Length == 0; } }
private void LoadMakes()
{
// ... do stuff...
this.Makes = this.MakesModels.Keys;
}
private void LoadModels(string make)
{
this.Models = [];
this.Model = string.Empty;
// ... do stuff...
this.Models = this.MakesModels[make];
}
private void MakeValueChanged(string make)
{
this.Make = make;
this.LoadModels(this.Make);
}
private void ModelValueChanged(string model)
{
this.Model = model;
}
private void OnSearchClick()
{
Console.WriteLine($"Searching for make '{this.Make}' and model '{this.Model}'");
}
protected override void OnInitialized()
{
this.LoadMakes();
}
}
这是一个(简单的)下拉组件实现示例,它按我的预期工作,但不使用 Fluent UI:
<div>
<label>@Label
<select id="@Id" @bind="Value" @bind:after="OnValueChanged">
@{
int i = 0;
foreach (var item in Items)
{
if (0 == i++)
{
<option value="" selected disabled>@PlaceholderText</option>
}
<option value="@item.Value">@item.Label</option>
}
}
</select>
</label>
</div>
如您所见,当项目列表更改时,
Placeholder
值会被重写。
我已经探索过使用 Fluent
Listbox
组件和 FluentSelect
组件来代替,但它们似乎没有 Placeholder
属性,并且总是选择与先前选择的值的位置相匹配的值(即在我看来奇怪的行为)。我想避免这种情况,并强制用户必须选择一个值,或者如果这是有效的选项,则根本不选择任何值。我想我可以使用 Listbox
并在开头添加一个空白条目。有没有更好的办法?
另请参阅:
谢谢,
凯恩
更新
我尝试在
FluentSelect
周围创建一个自定义组件包装器来处理占位符,但这似乎会产生一些奇怪的行为。例如, selected
的初始 Make
状态不会选择占位符(这可能是因为我已禁用它),并且 Model
值反映了任何先前选择的值的序数位置,无论如何更改列表项。
FluentSelect 包装组件
<FluentSelect
Label="@Label"
Items="@OptionItems"
TOption="Option<string>"
OptionText="@(i => i.Text)"
OptionValue="@(i => i.Value)"
OptionSelected="@(i => i.Selected)"
OptionDisabled="@(i => i.Disabled)"
ValueChanged="OnValueChanged"
Disabled="@Disabled" />
@code {
private IEnumerable<string> _items = [];
[Parameter]
public bool Disabled { get; set; }
[Parameter]
public IEnumerable<string> Items
{
get { return _items; }
set
{
_items = value;
OptionItems = new()
{
{ new() { Disabled = true, Selected = true, Text = Placeholder, Value = string.Empty } },
};
var options = _items.Select(item => new Option<string> { Text = item, Value = item });
OptionItems.AddRange(options);
}
}
[Parameter]
public string Label { get; set; } = string.Empty;
[Parameter]
public string Placeholder { get; set; } = string.Empty;
[Parameter]
public string Value { get; set; } = string.Empty;
[Parameter]
public EventCallback<string> OnValueChanged { get; set; }
private List<Option<string>> OptionItems = [];
}
您可以尝试以下方法
<h3>Vehicles</h3>
<FluentCombobox Label="Make:" Placeholder="Select a make..." Items="@MakesModels.Keys.ToList()" SelectedOptionChanged="@((p)=>{ Make=p; Model=null; })" SelectedOption="@Make" TOption=string Autocomplete="ComboboxAutocomplete.List" Height="300px" />
<br />
<FluentCombobox Label="Model:" Placeholder="Select a model..." Items="@(Make!=null?@MakesModels[Make]:[])" TOption=string SelectedOption="@Model" SelectedOptionChanged="@((p)=>Model=p)" Disabled="@(Make==null)" Autocomplete="ComboboxAutocomplete.List" Height="300px" />
<br />
<FluentButton @onclick="OnSearchClick" Disabled="@(Make==null||Model==null)">Search for @Make @Model</FluentButton>
@code {
public string? Make { get; set; }
public string? Model { get; set; }
private IDictionary<string, IEnumerable<string>> MakesModels { get; set; } = new Dictionary<string, IEnumerable<string>>
{
{"Audi", new string[] { "A4", "A6", "A8" } },
{"BMW", new string[] { "3 Series", "4 Series", "6 Series" } },
{"Volvo", new string[] { "C30", "C40", "C70" } },
};
private void OnSearchClick()
{
Console.WriteLine($"Searching for make '{this.Make}' and model '{this.Model}'");
}
}