选择和配置最合适的 Blazor Fluent UI 下拉菜单

问题描述 投票:0回答:1

我对 Blazor 和新的 Fluent UI 相当陌生。我正在尝试根据 API 中的值创建一组分层下拉列表(一个列表中的值基于另一个列表中的用户选择),但似乎无法让占位符文本按照我的方式运行喜欢。

使用以下 Blazor 代码,我模拟了我的问题的示例。它使用

FluentCombobox
因为它有一个方便的
Placeholder
属性。问题是,当子列表发生变化时,之前选择的值会被保留。

重现步骤:

  1. 页面加载后,选择品牌:'Audi'。
  2. 然后选择型号:'A4'。
  3. 然后返回“制造”并选择“BMW”。
  4. 问题:尽管“A4”不是有效选项,但模型中的值仍然反映“A4”。我希望它显示
    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 = [];
}

user-interface blazor fluent fluent-ui
1个回答
0
投票

您可以尝试以下方法

<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}'");
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.