RA7
RA7

Reputation: 1

Blazor FluentUI Virtualized Combobox: Display Text and Search Issues

I'm working on a Blazor Web App that interacts with my SQL Server, and I'm trying to create a reusable custom Razor component for a virtualized FluentCombobox.

The goal is to pass in a list of objects of any type, along with functions to extract the display text and return value for each item.

Here's the definition of my first example object, ParameterValue:

// ID of the parameter value  
public int? ID { get; set; }  
// display text to show in comboboxes  
public string ParameterText { get; set; }

Since I have a large number of items, rendering all of them right away led to some significant wait times, so I decided to add the virtualization. Additionally, I want users to be able to search/filter items by typing into the combobox. For this, I’m currently using an autocomplete approach. This worked fine before, but adding the virtualization introduced a couple of problems with the search.

Issues I'm facing:

Selected ID displayed instead of text: when I select an item, the combobox shows the ID instead of the display text.

Search behavior: Searching works for values currently displayed, but entering a character that isn't part of the current list empties the combobox.

Search lag: the search input seems to be one character behind.

For example:

Here's my current implementation for the Razor component:

@typeparam TOption

<FluentCombobox TOption="string" Placeholder="@Placeholder" Autocomplete="@Autocomplete" Height="@Height"
                @oninput="OnInputChanged"
                @onchange="OnItemSelected">

    @if (FilteredItems != null && FilteredItems.Count > 0)
    {
        <Virtualize Context="item" ItemsProvider="LoadItems" ItemSize="50" OverscanCount="5">
            <FluentOption Value="@GetItemValue(item)">
                @GetItemText(item)
            </FluentOption>
        </Virtualize>
    }
    else
    {
        <div>Loading...</div>
    }

</FluentCombobox>

@code {
    [Parameter] public List<TOption> Items { get; set; } = new List<TOption>();
    [Parameter] public string Placeholder { get; set; } = "Make a selection...";
    [Parameter] public ComboboxAutocomplete Autocomplete { get; set; } = ComboboxAutocomplete.Both;
    [Parameter] public string Height { get; set; } = "200px";
    [Parameter] public Func<TOption, string>? ItemTextSelector { get; set; }
    [Parameter] public Func<TOption, string>? ItemValueSelector { get; set; }
    [Parameter] public EventCallback<string> ValueChangedCallback { get; set; }
    [Parameter] public string ReturnFieldName { get; set; }

    private string? SelectedId { get; set; }
    private List<TOption> FilteredItems { get; set; } = new List<TOption>();

    protected override void OnInitialized()
    {
        FilteredItems = Items;
    }

    private async ValueTask<ItemsProviderResult<TOption>> LoadItems(ItemsProviderRequest request)
    {
        var totalItemCount = FilteredItems.Count;
        var requestedItems = FilteredItems.Skip(request.StartIndex).Take(request.Count).ToList();
        return new ItemsProviderResult<TOption>(requestedItems, totalItemCount);
    }

    private void OnInputChanged(ChangeEventArgs e)
    {
        var searchTerm = ((string)e.Value)?.ToString();

        if (!string.IsNullOrEmpty(searchTerm))
        {
            FilteredItems = Items
                .Where(i => GetItemText(i).Contains(searchTerm, StringComparison.OrdinalIgnoreCase))
                .ToList();
        }
        else
        {
            FilteredItems = new List<TOption>(Items);
        }

        StateHasChanged();
    }

    private void OnItemSelected(ChangeEventArgs e)
    {
        var selectedValue = e.Value?.ToString();
        TOption selectedItem = Items.FirstOrDefault(i => GetItemValue(i) == selectedValue);

        if (selectedItem != null)
        {
            ValueChangedCallback.InvokeAsync(GetPropertyValue(selectedItem, ReturnFieldName));
        }

        StateHasChanged();
    }

    private string GetPropertyValue(TOption item, string propertyName)
    {
        var propertyInfo = typeof(TOption).GetProperty(propertyName);
        return propertyInfo?.GetValue(item)?.ToString() ?? string.Empty;
    }

    private string GetItemText(TOption item)
    {
        return ItemTextSelector != null ? ItemTextSelector(item) : item?.ToString();
    }

    private string GetItemValue(TOption item)
    {
        return ItemValueSelector != null ? ItemValueSelector(item) : item?.ToString();
    }
}

I've tried debugging the input lag but couldn't figure out why it's one character behind. From what I found online, it seems to be an issue related to how Blazor updates the DOM vs. when the event is processed.

For the display issue, I suspect it might have something to do with how I'm binding the selected value.

Questions:

I'm quite new to Blazor and web development in general, so any advice or suggestions would be greatly appreciated!

If there's a better approach entirely or if you can point me toward useful resources, that would be very helpful as well.

Best regards
RA7

Upvotes: 0

Views: 214

Answers (0)

Related Questions