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

Grid Filter Menu

One of the filter modes of the grid is a popup menu with filter options that you can open from the column headers.

In this article:

Basics

To enable the filter menu, set the FilterMode property of the grid to Telerik.Blazor.GridFilterMode.FilterMenu.

The grid will render a button in the column header that you click to get a popup with filtering options. The popup lets you choose filter operator, filter criteria, to apply and clear the filter.

The filter is applied only upon a button click, not upon input change. This may improve performance if you use manual CRUD operations by reducing the number of requests compared to using the Filter Row.

Filter Menu in Telerik Grid

@* Filter menu in the column header *@

<TelerikGrid Data=@GridData FilterMode="Telerik.Blazor.GridFilterMode.FilterMenu"
             Pageable="true" Height="400px">
    <GridColumns>
        <GridColumn Field=@nameof(Employee.Name) />
        <GridColumn Field=@nameof(Employee.AgeInYears) Title="Age" />
        <GridColumn Field=@nameof(Employee.HireDate) Title="Hire Date" />
        <GridColumn Field=@nameof(Employee.IsOnLeave) Title="On Vacation" />
    </GridColumns>
</TelerikGrid>

@code {
    public List<Employee> GridData { get; set; }

    protected override void OnInitialized()
    {
        GridData = new List<Employee>();
        var rand = new Random();
        for (int i = 0; i < 100; i++)
        {
            GridData.Add(new Employee()
            {
                EmployeeId = i,
                Name = "Employee " + i.ToString(),
                AgeInYears = rand.Next(10, 80),
                HireDate = DateTime.Now.Date.AddDays(rand.Next(-20, 20)),
                IsOnLeave = i % 3 == 0
            });
        }
    }

    public class Employee
    {
        public int? EmployeeId { get; set; }
        public string Name { get; set; }
        public int? AgeInYears { get; set; }
        public DateTime HireDate { get; set; }
        public bool IsOnLeave { get; set; }
    }
}

The result from the code snippet above, after the "Age" column has been filtered with <= 30 operator.

Blazor Grid Filter Menu

Filter From Code

You can set the grid filters from your code through the grid state.

If you want to set an initial state to the grid, use a similar snippet, but in the OnStateInit event

Set filtering programmatically

@* This snippet shows how to set filtering state to the grid from your code
  Applies to the FilterMenu mode *@

@using Telerik.DataSource;

<TelerikButton ThemeColor="primary" OnClick="@SetGridFilter">set filtering from code</TelerikButton>

<TelerikGrid Data="@MyData" Height="400px" @ref="@Grid"
             Pageable="true" FilterMode="@GridFilterMode.FilterMenu">
    <GridColumns>
        <GridColumn Field="@(nameof(SampleData.Id))" Width="120px" />
        <GridColumn Field="@(nameof(SampleData.Name))" Title="Employee Name" />
        <GridColumn Field="@(nameof(SampleData.Team))" Title="Team" />
        <GridColumn Field="@(nameof(SampleData.HireDate))" Title="Hire Date" />
    </GridColumns>
</TelerikGrid>

@code {
    public TelerikGrid<SampleData> Grid { get; set; }

    async Task SetGridFilter()
    {
        GridState<SampleData> desiredState = new GridState<SampleData>()
        {
            FilterDescriptors = new List<IFilterDescriptor>()
            {
                new CompositeFilterDescriptor()
                {
                    FilterDescriptors = new FilterDescriptorCollection()
                    {
                         new FilterDescriptor() { Member = "Id", Operator = FilterOperator.IsGreaterThan, Value = 5, MemberType = typeof(int) },
                         new FilterDescriptor() { Member = "Id", Operator = FilterOperator.IsLessThan, Value = 20, MemberType = typeof(int) },
                    },
                    LogicalOperator = FilterCompositionLogicalOperator.And
                },
                new CompositeFilterDescriptor()
                {
                    FilterDescriptors = new FilterDescriptorCollection()
                    {
                        new FilterDescriptor() { Member = "Team", Operator = FilterOperator.Contains, Value = "3", MemberType = typeof(string) },
                    }
                }
            }
        };

        await Grid.SetStateAsync(desiredState);
    }

    public IEnumerable<SampleData> MyData = Enumerable.Range(1, 30).Select(x => new SampleData
    {
        Id = x,
        Name = "name " + x,
        Team = "team " + x % 5,
        HireDate = DateTime.Now.AddDays(-x).Date
    });

    public class SampleData
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Team { get; set; }
        public DateTime HireDate { get; set; }
    }
}

Until version 3.1.0, there was a bug that made the component create default FilterDecriptors in its state for all columns when FilterMode is FilterMenu, including when no filtering occurs at all. This side-effect allowed filter templates to work seamlessly and with little code. After version 3.1.0, ths bug was fixed and empty FilterDecriptors are not added to the state of the component if no filtering occurs (the way filter templates work is unchanged).

If you want to alter filters for a column, you must either modify the existing descriptor, or replace it. Simply adding an additional FilterDescriptor will not show up in the UI, because the Grid uses the first descriptor for the given field for the filtering UI. Another implication is that the Grid state always contains filter descriptors, no matter if the user has filtered or not. Inactive filter descriptors are distinguished by their null Value.

Handling filter changes - unexpected addition that does not update the UI, replacement of one filter, replacecment of all filters

@using Telerik.DataSource

To see the full effects of this behavior, filter a column such as the ID being less than or equal to 4.
<br /> Then, click a button to see the different behavior each will have:
<ul>
    <li>
        the first button will not show the new filter in the Team column header
        <TelerikButton OnClick="@WrongFilterAdd">Add filter without replace - you will not see the change in the filter UI but the data will change</TelerikButton>
    </li>
    <li>
        the second button will keep the ID column filter and add the Team filter
        <TelerikButton OnClick="@SetFilterWithReplaceInCollection">Replace only Team filter</TelerikButton>
    </li>
    <li>
        the third button will remove all filters and set only the custom Team filter
        <TelerikButton OnClick="@SetFilterNewCollection">Replace all filters to filter by Team</TelerikButton>
    </li>
</ul>

This flexibility lets you choose what behavior you want from the grid.



<TelerikGrid Data="@MyData" Height="400px" @ref="@Grid"
             Pageable="true" Sortable="true"
             FilterMode="@GridFilterMode.FilterMenu">
    <GridColumns>
        <GridColumn Field="@(nameof(SampleData.Id))" Width="120px" />
        <GridColumn Field="@(nameof(SampleData.Name))" Title="Employee Name" />
        <GridColumn Field="@(nameof(SampleData.Team))" Title="Team" />
        <GridColumn Field="@(nameof(SampleData.HireDate))" Title="Hire Date" />
    </GridColumns>
</TelerikGrid>



@code{
    // adds a filter without affecting the UI
    async Task WrongFilterAdd()
    {
        // get state
        var state = Grid.GetState();

        // simply add a filter to the existing state and existing (default) filters
        // PROBLEM - YOU WILL NOT SEE IT HIHGLIGHT THE UI BECAUSE THERE IS A DEFAULT FILTER ALREADY

        state.FilterDescriptors.Add(new CompositeFilterDescriptor()
        {
            FilterDescriptors = new FilterDescriptorCollection()
            {
                new FilterDescriptor
                {
                    Member = "Team",
                    Operator = FilterOperator.IsEqualTo,
                    Value = "team 1",
                    MemberType = typeof(string)
                },
                new FilterDescriptor()
                {
                    Member = "Team",
                    Operator = FilterOperator.IsEqualTo,
                    Value = null,
                    MemberType = typeof(string)
                }
            },
            LogicalOperator = FilterCompositionLogicalOperator.Or
        });

        // set state
        await Grid.SetStateAsync(state);
    }

    // adds a filter
    async Task SetFilterWithReplaceInCollection()
    {
        // get the current state
        var state = Grid.GetState();

        // remove the current filters for the Team field
        // if you don't do this, the default or exiting fitlers will command the UI
        // and you may not see the change from the new filter you will add
        // see the extension methods related to GetFiltersForMember in the adjacent code tab
        // create a new collection so we can iterate over it without it getting lost with the removal from the parent collection
        List<IFilterDescriptor> teamFilters = new List<IFilterDescriptor>(state.FilterDescriptors.GetFiltersForMember("Team"));

        // remove the existing filters for the Team field from the current collection
        for (int i = 0; i < teamFilters.Count(); i++)
        {
            state.FilterDescriptors.Remove(teamFilters.ElementAt(i) as IFilterDescriptor);
        }

        // create the desired new filter for the Team field
        CompositeFilterDescriptor theFilterDescriptor = new CompositeFilterDescriptor()
        {
            FilterDescriptors = new FilterDescriptorCollection()
            {
                new FilterDescriptor
                {
                    Member = "Team",
                    Operator = FilterOperator.IsEqualTo,
                    Value = "team 2",
                    MemberType = typeof(string)
                },
                new FilterDescriptor()
                {
                    Member = "Team",
                    Operator = FilterOperator.IsEqualTo,
                    Value = null,
                    MemberType = typeof(string)
                }
            },
            LogicalOperator = FilterCompositionLogicalOperator.Or
        };

        // add the new filter so that it replaces the original filter(s)
        state.FilterDescriptors.Add(theFilterDescriptor);

        // set the updated state
        await Grid.SetStateAsync(state);
    }

    // replaces all filters
    async Task SetFilterNewCollection()
    {
        // get the current state
        var state = Grid.GetState();

        //replace the entire filters collection so it only has the new desired filter
        state.FilterDescriptors = new List<IFilterDescriptor>()
        {
            new CompositeFilterDescriptor()
            {
                FilterDescriptors = new FilterDescriptorCollection()
                {
                    new FilterDescriptor()
                    {
                        Member = "Team",
                        Operator = FilterOperator.Contains,
                        Value = "3",
                        MemberType = typeof(string)
                    },
                    new FilterDescriptor()
                    {
                        Member = "Team",
                        Operator = FilterOperator.IsEqualTo,
                        Value = null,
                        MemberType = typeof(string)
                    }
                },
                LogicalOperator = FilterCompositionLogicalOperator.Or
            }
        };

        // set the new state
        await Grid.SetStateAsync(state);
    }
}

@code {
    TelerikGrid<SampleData> Grid { get; set; }

    public IEnumerable<SampleData> MyData = Enumerable.Range(1, 30).Select(x => new SampleData
    {
        Id = x,
        Name = "name " + x,
        Team = "Team " + x % 5,
        HireDate = DateTime.Now.AddDays(-x).Date
    });

    public class SampleData
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Team { get; set; }
        public DateTime HireDate { get; set; }
    }
}
using Telerik.DataSource;

//make sure the namespace matches your page, or add an appropriate using

public static class FilterExtensions
{
    public static IEnumerable<CompositeFilterDescriptor> GetCompositeFiltersForMember(this IEnumerable<IFilterDescriptor> filtersCollection, string member)
    {
        var compositeFilters = filtersCollection.OfType<CompositeFilterDescriptor>();
        return compositeFilters.GetFiltersForMember(member).Cast<CompositeFilterDescriptor>();
    }

    public static IEnumerable<IFilterDescriptor> GetAllFiltersForMember(this IEnumerable<IFilterDescriptor> filtersCollection, string member)
    {
        return filtersCollection.SelectMemberDescriptors().GetFiltersForMember(member);
    }

    public static IEnumerable<IFilterDescriptor> GetFiltersForMember(this IEnumerable<IFilterDescriptor> filtersCollection, string member)
    {
        return filtersCollection.Where(filter => filter.GetFilterMember() == member) ??
               Enumerable.Empty<IFilterDescriptor>();
    }

    public static string GetFilterMember(this IFilterDescriptor filter)
    {
        var filterDescriptor = filter;
        var isFilterDescriptor = filterDescriptor is FilterDescriptor;

        while (!isFilterDescriptor)
        {
            var compositeDescriptor = filterDescriptor as CompositeFilterDescriptor;

            if (compositeDescriptor == null)
            {
                break;
            }

            filterDescriptor = compositeDescriptor.FilterDescriptors?.ElementAtOrDefault(0);
            isFilterDescriptor = filterDescriptor is FilterDescriptor;
        }

        return ((FilterDescriptor)filterDescriptor)?.Member;
    }
}

Customization

The Grid allows you to customize the default behavior of the Filter Menu in a couple ways:

Configuring the Filter Menu

You can override the default Filter Row behavior for each column through the following property the GridColumn exposes:

Parameter Type and Default Value Description
DefaultFilterOperator FilterOperator Sets the default filter operator in the column it is declared for. Accepts a member of the FilterOperator enum. The selected operator must be applicable for the specific data type Check the supported options in the Filter Operators article. The provided default filter operator will be applied for both filters in the menu.
FilterOperators List<FilterListOperator> Specifies the available operators. Must contain only supported filter operators for the specific data type. If not defined, the component will use a default list of available operators based on the field type.

Configure the Filter Menu

@*Customize the Filter Menu*@

@using Telerik.DataSource

<TelerikGrid Data="@MyData"
             Height="400px"
             Pageable="true"
             FilterMode="@GridFilterMode.FilterMenu">
    <GridColumns>
        <GridColumn DefaultFilterOperator="FilterOperator.IsEqualTo"
                    Field="@(nameof(SampleData.Id))" Width="120px" />
        <GridColumn DefaultFilterOperator="FilterOperator.StartsWith"
                    Field="@(nameof(SampleData.Name))" Title="Employee Name" />
        <GridColumn DefaultFilterOperator="FilterOperator.Contains"
                    Field="@(nameof(SampleData.Team))" Title="Team" />
        <GridColumn DefaultFilterOperator="FilterOperator.IsGreaterThanOrEqualTo"
                    Field="@(nameof(SampleData.HireDate))" Title="Hire Date" />
    </GridColumns>
</TelerikGrid>

@code {
    public IEnumerable<SampleData> MyData = Enumerable.Range(1, 30).Select(x => new SampleData
        {
            Id = x,
            Name = "name " + x,
            Team = "team " + x % 5,
            HireDate = DateTime.Now.AddDays(-x).Date
        });

    public class SampleData
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Team { get; set; }
        public DateTime HireDate { get; set; }
    }
}

Filter Menu Template

The template will let you have full control over the Filter Row rendering and behavior. See how you can implement it and explore the example in the Filter Menu Template article.

See Also

In this article