Consider this test page: https://blazorrepl.telerik.com/cSEQOTQY53LRL4je36
This happens only when using a column menu - ShowColumnMenu="true".
===
A possible workaround is to use a FilterButtonsTemplate and clear the filters programmatically if the filter descriptor is empty:
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
<TelerikGrid TItem="@Employee"
OnRead="@OnReadHandler"
Pageable="true"
FilterMode="@GridFilterMode.FilterMenu"
FilterMenuType="@FilterMenuType.CheckBoxList"
ShowColumnMenu="true"
Height="400px">
<GridColumns>
<GridColumn Field="@(nameof(Employee.EmployeeId))" Filterable="false" />
<GridColumn Field="@nameof(Employee.Name)">
<FilterMenuTemplate Context="context">
<TelerikCheckBoxListFilter Data="@NameOptions"
Field="@(nameof(NameFilterOption.Name))"
@bind-FilterDescriptor="@context.FilterDescriptor">
</TelerikCheckBoxListFilter>
</FilterMenuTemplate>
<FilterMenuButtonsTemplate Context="filterContext">
<TelerikButton OnClick="@( async () => await ApplyFilterAsync(filterContext) )"
ThemeColor="primary">Filter</TelerikButton>
<TelerikButton OnClick="@( async () => await ClearFilterAsync(filterContext) )">Clear</TelerikButton>
</FilterMenuButtonsTemplate>
</GridColumn>
<GridColumn Field="@nameof(Employee.Team)" Title="Team">
<FilterMenuTemplate Context="context">
<TelerikCheckBoxListFilter Data="@TeamsList"
Field="@(nameof(TeamNameFilterOption.Team))"
@bind-FilterDescriptor="@context.FilterDescriptor">
</TelerikCheckBoxListFilter>
</FilterMenuTemplate>
</GridColumn>
<GridColumn Field="@nameof(Employee.IsOnLeave)" Title="On Vacation" />
</GridColumns>
</TelerikGrid>
@code {
List<Employee> AllGridData { get; set; }
#region custom-filter-data
List<TeamNameFilterOption> TeamsList { get; set; }
List<NameFilterOption> NameOptions { get; set; }
private async Task ApplyFilterAsync(FilterMenuTemplateContext filterContext)
{
var hasFilters = filterContext.FilterDescriptor.FilterDescriptors.OfType<FilterDescriptor>().Any(x => !string.IsNullOrEmpty(x.Value.ToString()));
if (hasFilters)
{
await filterContext.FilterAsync();
}
else
{
await filterContext.ClearFilterAsync();
}
}
private async Task ClearFilterAsync(FilterMenuTemplateContext filterContext)
{
await filterContext.ClearFilterAsync();
}
//obtain filter lists data from the data source to show all options
async Task GetTeamOptions()
{
if (TeamsList == null) // sample of caching since we always want all distinct options,
//but we don't want to make unnecessary requests
{
TeamsList = await GetNamesFromService();
}
}
async Task<List<TeamNameFilterOption>> GetNamesFromService()
{
await Task.Delay(500);// simulate a real service delay
// this is just one example of getting distinct values from the full data source
// in a real case you'd probably call your data service here instead
// or apply further logic (such as tie the returned data to the data the grid will have according to your business logic)
List<TeamNameFilterOption> data = AllGridData.OrderBy(z => z.Team).Select(z => z.Team).
Distinct().Select(t => new TeamNameFilterOption { Team = t }).ToList();
return await Task.FromResult(data);
}
async Task GetNameOptions()
{
if (NameOptions == null)
{
NameOptions = await GetNameOptionsFromService();
}
}
async Task<List<NameFilterOption>> GetNameOptionsFromService()
{
await Task.Delay(500);// simulate a real service delay
List<NameFilterOption> data = AllGridData.OrderBy(z => z.Name).Select(z => z.Name).
Distinct().Select(n => new NameFilterOption { Name = n }).ToList();
return await Task.FromResult(data);
}
#endregion custom-filter-data
async Task OnReadHandler(GridReadEventArgs args)
{
//typical data retrieval for the grid
var filteredData = await AllGridData.ToDataSourceResultAsync(args.Request);
args.Data = filteredData.Data as IEnumerable<Employee>;
args.Total = filteredData.Total;
}
protected override async Task OnInitializedAsync()
{
AllGridData = new List<Employee>();
var rand = new Random();
for (int i = 1; i <= 15; i++)
{
AllGridData.Add(new Employee()
{
EmployeeId = i,
Name = "Employee " + i.ToString(),
Team = "Team " + i % 3,
IsOnLeave = i % 2 == 0
});
}
await GetTeamOptions();
await GetNameOptions();
}
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string Team { get; set; }
public bool IsOnLeave { get; set; }
}
// in this sample we use simplified models to fetch less data from the service
// instead of using the full Employee model that has many fields we do not need for the filters
public class TeamNameFilterOption
{
public string Team { get; set; }
}
public class NameFilterOption
{
public string Name { get; set; }
}
}