Add AutoApplyCheckBoxListFilter to apply CheckBoxList filters on selection (#2554)

This commit is contained in:
Josh
2026-05-20 22:14:57 -07:00
committed by GitHub
parent f36700a4ab
commit 891ba9bae7
6 changed files with 180 additions and 25 deletions

View File

@@ -3489,6 +3489,94 @@ namespace Radzen.Blazor.Tests
Assert.Contains("Nothing to display", component.Markup);
}
private sealed class AutoApplyItem
{
public string Name { get; set; } = string.Empty;
public int Code { get; set; }
}
[Fact]
public void DataGrid_AutoApplyCheckBoxListFilter_FiltersOnSelectionWithoutApply()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var data = new[]
{
new AutoApplyItem { Name = "A", Code = 1 },
new AutoApplyItem { Name = "B", Code = 2 },
new AutoApplyItem { Name = "C", Code = 1 },
};
var component = ctx.RenderComponent<RadzenDataGrid<AutoApplyItem>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<AutoApplyItem>>(p => p.Data, data);
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.CheckBoxList);
parameterBuilder.Add<bool>(p => p.AutoApplyCheckBoxListFilter, true);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<AutoApplyItem>));
builder.AddAttribute(1, "Property", nameof(AutoApplyItem.Code));
builder.CloseComponent();
});
});
// Open the Code filter popup (the filter icon toggles via @onmousedown=ToggleFilter)
component.Find("button.rz-grid-filter-icon").MouseDown();
// Wait for the listbox to load its filter values asynchronously.
component.WaitForAssertion(() => Assert.NotEmpty(component.FindAll(".rz-multiselect-item")), TimeSpan.FromSeconds(3));
// Apply button must NOT be present in auto-apply mode
Assert.Empty(component.FindAll("button.rz-apply-filter"));
// Select the option for Code == 1 in the listbox and confirm the grid filters immediately.
// Find the listbox item whose text is "1" and click it.
var item = component.FindAll(".rz-multiselect-item")
.FirstOrDefault(i => i.TextContent.Trim() == "1");
Assert.NotNull(item);
item!.Click();
var grid = component.Instance;
var codes = ((System.Collections.IEnumerable)grid.View).Cast<AutoApplyItem>().Select(x => x.Code).Distinct().ToArray();
Assert.Equal(new[] { 1 }, codes);
// The popup must stay open after auto-apply so additional options can be selected.
// Applying the filter with closePopup:false means no popup-closing JS interop runs;
// closePopup:true would invoke Radzen.closePopup / Radzen.closeAllPopups.
Assert.DoesNotContain(ctx.JSInterop.Invocations,
i => i.Identifier.Contains("close", StringComparison.OrdinalIgnoreCase));
}
[Fact]
public void DataGrid_CheckBoxListFilter_RendersApplyButton_WhenAutoApplyOff()
{
using var ctx = new TestContext();
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
var data = new[] { new AutoApplyItem { Name = "A", Code = 1 } };
var component = ctx.RenderComponent<RadzenDataGrid<AutoApplyItem>>(parameterBuilder =>
{
parameterBuilder.Add<IEnumerable<AutoApplyItem>>(p => p.Data, data);
parameterBuilder.Add<bool>(p => p.AllowFiltering, true);
parameterBuilder.Add<FilterMode>(p => p.FilterMode, FilterMode.CheckBoxList);
parameterBuilder.Add<RenderFragment>(p => p.Columns, builder =>
{
builder.OpenComponent(0, typeof(RadzenDataGridColumn<AutoApplyItem>));
builder.AddAttribute(1, "Property", nameof(AutoApplyItem.Code));
builder.CloseComponent();
});
});
component.Find("button.rz-grid-filter-icon").MouseDown();
Assert.NotEmpty(component.FindAll("button.rz-apply-filter"));
}
private sealed class TrackingEnumerable<T> : IEnumerable<T>
{
private readonly IEnumerable<T> _source;

View File

@@ -1611,6 +1611,14 @@ namespace Radzen.Blazor
[Parameter]
public bool AllowFiltering { get; set; }
/// <summary>
/// Gets or sets whether CheckBoxList column filters are applied immediately as options are
/// selected, instead of requiring the filter popup's Apply button. Default is false.
/// </summary>
/// <value>True to apply CheckBoxList filters on each selection change; otherwise false.</value>
[Parameter]
public bool AutoApplyCheckBoxListFilter { get; set; }
/// <summary>
/// Gets or sets a value indicating whether column resizing is allowed.
/// </summary>

View File

@@ -188,7 +188,10 @@
{
<div class="rz-grid-filter-buttons">
<RadzenButton ButtonStyle="ButtonStyle.Base" class="rz-clear-filter" Click="@ClearFilter" Text=@Grid.ClearFilterText title="@Grid.ClearFilterText" />
<RadzenButton onmousedown="@getBlur()" ButtonStyle="ButtonStyle.Primary" class="rz-apply-filter" form="@($"{Column.GetColumnPopupID()}-form")" ButtonType="ButtonType.Submit" Text=@Grid.ApplyFilterText title="@Grid.ApplyFilterText" />
@if (!(Grid.AutoApplyCheckBoxListFilter && filterMode == FilterMode.CheckBoxList))
{
<RadzenButton onmousedown="@getBlur()" ButtonStyle="ButtonStyle.Primary" class="rz-apply-filter" form="@($"{Column.GetColumnPopupID()}-form")" ButtonType="ButtonType.Submit" Text=@Grid.ApplyFilterText title="@Grid.ApplyFilterText" />
}
</div>
}
</Popup>
@@ -350,43 +353,52 @@ else
return string.IsNullOrWhiteSpace(Column.HeaderTooltip) ? null : Column.HeaderTooltip;
}
void ListBoxChange(object args)
async Task ListBoxChange(object args)
{
hasPendingCheckBoxFilter = true;
if (args is not IEnumerable enumerable)
{
pendingCheckBoxFilterValue = null;
return;
}
Type? propertyType = null;
if (Column.Property != Column.FilterProperty && !string.IsNullOrEmpty(Column.FilterProperty))
{
var collectionType = PropertyAccess.GetPropertyType(typeof(TItem), Column.Property);
if (collectionType != null && collectionType.IsGenericType)
{
var itemType = collectionType.GenericTypeArguments.FirstOrDefault();
if(itemType != null)
{
propertyType = PropertyAccess.GetPropertyType(itemType, Column.FilterProperty);
}
}
}
else
{
propertyType = PropertyAccess.GetPropertyType(typeof(TItem), Column.GetFilterProperty());
Type? propertyType = null;
if (Column.Property != Column.FilterProperty && !string.IsNullOrEmpty(Column.FilterProperty))
{
var collectionType = PropertyAccess.GetPropertyType(typeof(TItem), Column.Property);
if (collectionType != null && collectionType.IsGenericType)
{
var itemType = collectionType.GenericTypeArguments.FirstOrDefault();
if(itemType != null)
{
propertyType = PropertyAccess.GetPropertyType(itemType, Column.FilterProperty);
}
}
}
else
{
propertyType = PropertyAccess.GetPropertyType(typeof(TItem), Column.GetFilterProperty());
}
if (propertyType == null)
{
propertyType = Column.FilterPropertyType;
}
pendingCheckBoxFilterValue = enumerable.Cast<object>().Any()
? propertyType != null ? QueryableExtension.ToList(enumerable.AsQueryable().Cast(propertyType)) : enumerable
: null;
}
if (propertyType == null)
if (Grid.AutoApplyCheckBoxListFilter)
{
propertyType = Column.FilterPropertyType;
Column.SetFilterValue(pendingCheckBoxFilterValue);
hasPendingCheckBoxFilter = false;
pendingCheckBoxFilterValue = null;
await Grid.ApplyFilter(Column, false);
}
pendingCheckBoxFilterValue = enumerable.Cast<object>().Any()
? propertyType != null ? QueryableExtension.ToList(enumerable.AsQueryable().Cast(propertyType)) : enumerable
: null;
}
string getFilterOpen()

View File

@@ -0,0 +1,26 @@
<RadzenDataGrid AllowFiltering="true" FilterMode="FilterMode.CheckBoxList" AutoApplyCheckBoxListFilter="true"
AllowPaging="true" PageSize="5" Data="@orders" ColumnWidth="200px">
<Columns>
<RadzenDataGridColumn Property="@nameof(Order.Reference)" Title="Reference" />
<RadzenDataGridColumn Property="@nameof(Order.Status)" Title="Status" />
</Columns>
</RadzenDataGrid>
@code {
class Order
{
public string Reference { get; set; } = "";
public string Status { get; set; } = "";
}
readonly List<Order> orders = new()
{
new Order { Reference = "ORD-001", Status = "Open" },
new Order { Reference = "ORD-002", Status = "Shipped" },
new Order { Reference = "ORD-003", Status = "Closed" },
new Order { Reference = "ORD-004", Status = "Open" },
new Order { Reference = "ORD-005", Status = "Shipped" },
new Order { Reference = "ORD-006", Status = "Closed" }
};
}

View File

@@ -0,0 +1,13 @@
@page "/datagrid-checkboxlist-auto-apply-filter"
<RadzenText TextStyle="TextStyle.H2" TagName="TagName.H1" class="rz-pt-8">
DataGrid <strong>CheckBoxList Filter Auto-Apply</strong>
</RadzenText>
<RadzenText TextStyle="TextStyle.Subtitle1" TagName="TagName.P" class="rz-pb-4">
Set AutoApplyCheckBoxListFilter on the grid to apply CheckBoxList column filters immediately as
options are selected, without the filter popup's Apply button.
</RadzenText>
<RadzenExample ComponentName="DataGrid" Example="DataGridCheckBoxListAutoApplyFilter">
<DataGridCheckBoxListAutoApplyFilter />
</RadzenExample>

View File

@@ -611,6 +611,14 @@ namespace RadzenBlazorDemos
Tags = new [] { "filter", "excel", "grid", "datagrid", "table", "menu", "checkbox", "list" }
},
new Example
{
Name = "CheckBoxList Auto-Apply",
Path = "datagrid-checkboxlist-auto-apply-filter",
Title = "Blazor DataGrid - CheckBoxList Filter Auto-Apply | Free UI Components by Radzen",
Description = "Apply CheckBoxList column filters immediately as options are selected, without the Apply button.",
Tags = new [] { "checkboxlist", "auto", "apply", "filter", "filtering", "datagrid", "table", "dataview" }
},
new Example
{
Name = "CheckBoxList with OData",
Path = "datagrid-checkboxlist-filter-odata",