New to Telerik UI for Blazor? Download free 30-day trial

ComboBox in Grid FilterMenuTemplate is Empty the First Time it is Opened

Environment

Product Grid for Blazor
Version 2.30 or older

Description

I have a ComboBox in a Grid FilterMenuTemplate. The ComboBox data is loaded asynchronously. The first time I open it, the dropdown is empty and reads and No Data. The second time the ComboBox is opened, the data shows as expected.

Possible Cause

The behavior is related to the filter menu popup. It is rendered outside the Grid component in the page <body>. If the ComboBox data is loaded asynchronously, the popup is not refreshed even by StateHasChanged.

Suggested Workarounds

  • Upgrade to UI for Blazor 3.0 or later. It uses different OnRead mechanism for loading asynchronous data and updating the ComboBox. The component dropdown will update even after it has been opened.
  • Load the ComboBox data before the filter menu is opened for the first time. Use Blazor events like OnInitializedAsync, or Grid events like OnStateInit or OnRead.
  • Load the ComboBox data synchronously.
  • Enable Grid filtering with a delay, after the ComboBox data has been loaded.

Here is a test page that loads the FilterMenuTemplate data in OnRead and enables Grid filtering afterwards:

<TelerikGrid Data="@Vehicles"
             OnRead="@ReadVehicles"
             FilterMode="@FlexibleFilterMode">
    <GridColumns>
        <GridColumn Field="@nameof(VehicleModel.Make)" Title="Vehicle Make">
            <FilterMenuTemplate>
                <TelerikComboBox Data="@VehicleMakes"
                                 Value="string.Empty">
                </TelerikComboBox>
            </FilterMenuTemplate>
        </GridColumn>
    </GridColumns>
</TelerikGrid>

@code {

    private List<string> VehicleMakes { get; set; } = new List<string>();

    private List<VehicleModel> Vehicles { get; set; }

    private GridFilterMode FlexibleFilterMode { get; set; } = GridFilterMode.None;

    async Task ReadVehicles(GridReadEventArgs args)
    {
        Vehicles = new List<VehicleModel>() {
            new VehicleModel { VehicleID = 1, Make = "Honda" }
        };

        await this.RefreshMakesAsync();
    }

    private async Task RefreshMakesAsync()
    {
        await Task.Delay(1000);
        VehicleMakes = new List<string>() { "Honda" };
        FlexibleFilterMode = GridFilterMode.FilterMenu;
    }

    public class VehicleModel
    {
        public int VehicleID { get; set; }
        public string Make { get; set; }
    }
}

Here is a test page that reproduces the issue and shows the possible workarounds with synchronous ComboBox data load, or asynchronous load in OnInitializedAsync.

<TelerikGrid Data="@Vehicles"
             FilterMode="@GridFilterMode.FilterMenu">
    <GridColumns>
        <GridColumn Field="@nameof(VehicleModel.Make)" Title="Vehicle Make">
            <FilterMenuTemplate>
                <TelerikComboBox Data="@VehicleMakes"
                                 OnRead="@OnReadMakes"
                                 Value="string.Empty">
                </TelerikComboBox>
            </FilterMenuTemplate>
        </GridColumn>
    </GridColumns>
</TelerikGrid>

@code {

    private List<string> VehicleMakes { get; set; } = new List<string>();

    private List<VehicleModel> Vehicles { get; set; } = new List<VehicleModel>() {
        new VehicleModel { VehicleID = 1, Make = "Honda" }
    };

    async Task OnReadMakes(ComboBoxReadEventArgs args)
    {
        if (!VehicleMakes.Any())
        {
            await this.RefreshMakesAsync();
            this.StateHasChanged();
        }
    }

    async Task RefreshMakesAsync()
    {
        // Comment next line to load the ComboBox data synchronously.
        await Task.Delay(1);
        VehicleMakes = new List<string>() { "Honda" };
    }

    protected override Task OnInitializedAsync()
    {
        // Uncomment next line to load the ComboBox data before the filter menu is opened.
        //VehicleMakes = new List<string>() { "Honda" };

        return base.OnInitializedAsync();
    }

    public class VehicleModel
    {
        public int VehicleID { get; set; }
        public string Make { get; set; }
    }
}
In this article