Filtering
Static server-side rendering: no circuit, no WebAssembly.
Column filters
Use Filterable on a column for a filter in its header. Text matches anywhere, a date its day, enums and booleans get a list of checkboxes, and a number takes a range: at least, at most, or both.
Preview
Code
@* Column filters: a funnel in each Filterable header opens the filter, and is marked while it's
in use. FilterPanel lists them all below the rows. Text matches anywhere, enums get a list of checkboxes, numbers take a range. *@
<Table Items="Orders.All" PageSize="5" InitialSort="-number" Caption="Filtered orders" QueryPrefix="orders-" FilterPanel Grow>
<PropertyColumn Property="o => o.Number" Title="Order" Align="CellAlign.Start" Sortable />
<PropertyColumn Property="o => o.Customer" Sortable Filterable />
<PropertyColumn Property="o => o.Status" Sortable Filterable />
<PropertyColumn Property="o => o.Total" Format="C" Sortable Filterable />
</Table>Static: Filters apply as you type.
The funnel is marked while its column is filtered, and Clear in its panel removes that filter. Use FilterPanel for a row below the table that lists every filter, with Clear for all of them; it's always there, so filtering doesn't move anything.
Lists of values
An enum or bool column's filter is a list of checkboxes. Every box ticked is no filter, and none ticked shows no rows.
Preview
Code
@* An enum or bool column's filter is a list: all ticked is no filter, and none ticked shows no rows.
A nullable column gets a (Blank) row, and a list of more than ten gets a search. *@
<Table Items="_tickets" PageSize="5" Caption="Support tickets" QueryPrefix="lists-" Grow>
<PropertyColumn Property="t => t.Subject" />
<PropertyColumn Property="t => t.Team" Sortable Filterable />
<PropertyColumn Property="t => t.Priority" Sortable Filterable />
<PropertyColumn Property="t => t.Open" Filterable />
</Table>
{
private enum Team { Billing, Design, Engineering, Finance, Legal, Marketing, Operations, Product, Research, Sales, Security, Support }
private enum Priority { High, Medium, Low }
private sealed record Ticket(string Subject, Team Team, Priority? Priority, bool Open);
private static readonly Ticket[] _tickets =
[
new("Refund not received", Team.Billing, Priority.High, true),
new("New logo files", Team.Design, Priority.Low, false),
new("Export times out", Team.Engineering, Priority.High, true),
new("Invoice has the wrong address", Team.Finance, Priority.Medium, true),
new("Terms of service question", Team.Legal, null, false),
new("Newsletter bounced", Team.Marketing, Priority.Low, true),
new("Server moved", Team.Operations, null, false),
new("Dark mode request", Team.Product, Priority.Low, true),
new("Survey results", Team.Research, Priority.Medium, false),
new("Quote for fifty seats", Team.Sales, Priority.High, true),
new("Password reset fails", Team.Security, Priority.High, true),
new("Can't sign in", Team.Support, Priority.Medium, true),
];
}Static: Ticks apply after a short pause.
A text column can list its values too. FilterList reads them from Items, and FilterValues finds them yourself, as from a database, with FilterValuesRequest.ToResultAsync doing the work. Each list shows what the other filters leave, and keeps what's ticked listed so it can be unticked.
Preview
Code
@* FilterList reads a text column's values from Items. FilterValues finds them yourself, as from a
database: it gets what's typed in the list's box and what the other filters leave. *@
<Table Items="_orders" PageSize="5" Caption="Orders by customer" QueryPrefix="values-" Grow>
<PropertyColumn Property="o => o.Number" Title="Order" />
<PropertyColumn Property="o => o.Customer" Sortable Filterable FilterValues="LoadCustomers" />
<PropertyColumn Property="o => o.City" Sortable Filterable FilterList />
<PropertyColumn Property="o => o.Region" Filterable />
</Table>
{
private enum Region { East, West }
private sealed record Order(int Number, string Customer, string? City, Region Region);
private static readonly Order[] _orders = Enumerable.Range(1, 600)
.Select(i => new Order(1000 + i, $"Customer {i:000}", i % 25 == 0 ? null : $"City {i % 12:00}", i % 2 == 0 ? Region.East : Region.West))
.ToArray();
private static readonly IReadOnlyList<DataField<Order>> _fields = DataFields.Of<Order>();
// With a database, r.ToResultAsync(db.Orders, _fields, (q, ct) => q.ToListAsync(ct)).
private static ValueTask<FilterValuesResult> LoadCustomers(FilterValuesRequest r) =>
ValueTask.FromResult(r.ToResult(_orders.AsQueryable(), _fields));
}Static: The script asks this page's own address for the values, with one more key, and takes the list from the response.
Filters in the address
Filters go in the URL as ?f-customer=ada (in the Column filters example, ?orders-f-customer=ada: that table has a QueryPrefix, so it sorts and pages apart from the others on the page, as under Several tables on a page). A number's two ends go in as ?f-total=100&f-total-to=500, so the form needs no script. For filters of your own, use a FilterForm above the table.
Choosing an operator
Use FilterOperators to choose an operator in a column's filter, as a DataGrid's columns do by default, or to limit the ones it offers. The first is the default, and a condition with any other matches nothing, even in an address written by hand. It goes in the address when it isn't the default, with its value: ?f-total-op=between&f-total=100&f-total-to=500.
Preview
Code
@* FilterOperators puts an operator above a column's value; the first is the default. Without it, a
Table's column keeps its simple field, as Status does. *@
<Table Items="Orders.All" PageSize="5" InitialSort="-number" Caption="Orders by operator" QueryPrefix="ops-" Grow>
<PropertyColumn Property="o => o.Number" Title="Order" Align="CellAlign.Start" Sortable />
<PropertyColumn Property="o => o.Customer" Sortable Filterable
FilterOperators="[FilterOperator.Contains, FilterOperator.StartsWith, FilterOperator.Equals]" />
<PropertyColumn Property="o => o.Status" Sortable Filterable />
<PropertyColumn Property="o => o.Total" Format="C" Sortable Filterable
FilterOperators="[FilterOperator.GreaterThanOrEqual, FilterOperator.LessThanOrEqual, FilterOperator.Between]" />
</Table>Filters in code
The filter is a Filter: Filter.All of one condition per filtered column (contains for text, between for a number, equals for a date, in for a choice, whose Values are what's ticked, with null for the blank row and none for nothing ticked). Set one in C# with @bind-Filter to filter from code, on an interactive page. A condition no column's panel can show, such as an or, still applies, and shows as a tag that removes it:
<Table Items="orders" @bind-Filter="filter">...</Table>
{
Filter? filter = Filter.Where("status", FilterOperator.Equals, "Paid");
}Data and filters has the operators and the filter syntax, and Loading data how a column's condition reaches the database.