CleanCodeJN.GenericApis.DataGrid
1.3.4
dotnet add package CleanCodeJN.GenericApis.DataGrid --version 1.3.4
NuGet\Install-Package CleanCodeJN.GenericApis.DataGrid -Version 1.3.4
<PackageReference Include="CleanCodeJN.GenericApis.DataGrid" Version="1.3.4" />
<PackageVersion Include="CleanCodeJN.GenericApis.DataGrid" Version="1.3.4" />
<PackageReference Include="CleanCodeJN.GenericApis.DataGrid" />
paket add CleanCodeJN.GenericApis.DataGrid --version 1.3.4
#r "nuget: CleanCodeJN.GenericApis.DataGrid, 1.3.4"
#:package CleanCodeJN.GenericApis.DataGrid@1.3.4
#addin nuget:?package=CleanCodeJN.GenericApis.DataGrid&version=1.3.4
#tool nuget:?package=CleanCodeJN.GenericApis.DataGrid&version=1.3.4
CleanCodeJN.GenericApis.DataGrid
A generic, server-side MudBlazor table component for CleanCodeJN.GenericApis.
Drop <CCJNDataGrid> onto any Blazor page and point it at your GraphQL endpoint — columns, types, paging, sorting and search are all handled automatically.
Requirements
- The backend must use
CleanCodeJN.GenericApiswith HotChocolate's auto-wiring (UseProjection,UseFiltering,UseSorting) - MudBlazor must be configured in the host app (
wwwroot/index.html/App.razor)
Installation
dotnet add package CleanCodeJN.GenericApis.DataGrid
Setup
Register the services in Program.cs:
builder.Services.AddCleanCodeJNDataGrid();
This registers MudServices, the named HttpClient and GraphQLDataGridService in one call.
Add the MudBlazor layout components to your App.razor or MainLayout.razor if not already present:
<MudThemeProvider />
<MudPopoverProvider />
<MudDialogProvider />
<MudSnackbarProvider />
Usage
@using CleanCodeJN.GenericApis.DataGrid.Components
<CCJNDataGrid TDto="CustomerGetDto"
GraphQLEndpoint="https://your-api/graphql"
EntityName="customer"
Title="Customers" />
EntityName is the lowercase GraphQL query field name — matches typeof(TEntity).Name.ToLowerInvariant() as generated by AutoQueryTypeExtensions.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
GraphQLEndpoint |
string |
yes | — | Full URL of the GraphQL endpoint |
EntityName |
string |
yes | — | Lowercase GraphQL query field name (e.g. "customer") |
Title |
string |
no | "" |
Heading rendered above the table |
Searchable |
bool |
no | true |
Show the search text field |
Dense |
bool |
no | false |
Compact row height |
Striped |
bool |
no | true |
Alternating row colours |
Elevation |
int |
no | 2 |
MudPaper shadow elevation |
BearerToken |
string? |
no | null |
Forwarded as Authorization: Bearer … header |
ExcludedProperties |
HashSet<string> |
no | [] |
DTO property names to hide (e.g. navigation properties) |
PageSizeOptions |
int[] |
no | [10,25,50,100] |
Page-size choices in the pager footer |
ColumnHeaders |
Dictionary<string,string>? |
no | null |
Custom column headers — maps property name → display label |
HiddenProperties |
HashSet<string> |
no | [] |
Requested from the server but given no column — for values a row dialog or cell template needs |
Hover |
bool |
no | true |
Highlight the row under the cursor |
OnRowClick |
EventCallback<TDto> |
no | — | Raised with the clicked row's item; the edit and delete buttons do not trigger it |
RowClass |
string? |
no | null |
CSS class on every row — e.g. cursor-pointer for a clickable table |
RowStyle |
string? |
no | null |
Inline style on every row |
RowClassFunc |
Func<TDto,int,string>? |
no | null |
Per-row CSS class, added on top of RowClass |
RowStyleFunc |
Func<TDto,int,string>? |
no | null |
Per-row inline style, added on top of RowStyle |
ViewFormContent |
RenderFragment<TDto>? |
no | null |
Read-only detail dialog opened by a row click, with nothing but a close button |
ViewTitle |
string? |
no | null |
Heading of that dialog; falls back to Title |
CloseLabel |
string |
no | "Close" |
Label of that dialog's only button |
Columns |
RenderFragment? |
no | null |
Explicit CCJNColumn children; replaces auto-detection |
EditOnRowClick |
bool |
no | false |
A row click opens the edit dialog; the pencil button is dropped |
SubmitLabel |
string |
no | "Save" |
Save button in the add and edit dialogs |
CancelLabel |
string |
no | "Cancel" |
Cancel button in every dialog |
DeleteLabel |
string |
no | "Delete" |
Confirming button in the delete dialog |
DeleteTitle |
string |
no | "Confirm Delete" |
Heading of the delete dialog |
AddTitle |
string? |
no | null |
Heading of the add dialog; falls back to Add {Title} |
EditTitle |
string? |
no | null |
Heading of the edit dialog; falls back to Edit {Title} |
SearchLabel |
string |
no | "Search..." |
Placeholder of the search field |
NoRecordsLabel |
string |
no | "No entries found..." |
Shown when the query came back empty |
LoadingLabel |
string |
no | "Data will be loaded..." |
Shown while rows are being fetched |
DeleteConfirmText |
string |
no | "Are you sure…" |
Question asked before deleting |
Clickable rows
OnRowClick gives you the item, RowClass the cursor:
<CCJNDataGrid TDto="InvoiceGetDto" ...
RowClass="cursor-pointer"
OnRowClick="OpenAsync" />
For a plain detail view there is no need to write a dialog at all — ViewFormContent opens one
that carries just a close button:
<CCJNDataGrid TDto="InvoiceGetDto" ...
RowClass="cursor-pointer"
ViewTitle="Invoice"
CloseLabel="Close"
HiddenProperties="@(new HashSet<string> { "Notes" })">
<ViewFormContent Context="row">
<MudText Typo="Typo.subtitle2">@row.Number</MudText>
<MudText Typo="Typo.body2" Style="white-space:pre-wrap">@row.Notes</MudText>
</ViewFormContent>
</CCJNDataGrid>
The grid carries several templated parameters, so Blazor wants each fragment's item named —
Context="row" above. Writing @context without it does not compile.
Notes is listed under HiddenProperties: the dialog needs the value, the table does not need the
column. ExcludedProperties would drop it from the query as well and the dialog would stay empty.
The same switches exist on CCJNDataGridDialog itself (ShowSubmit, ShowCancel, CloseLabel)
if you open it yourself.
Where rows are editable, a read-only view is usually one dialog too many. EditOnRowClick sends
the click straight into the edit dialog and drops the pencil button; the action column disappears
entirely once nothing is left in it:
<CCJNDataGrid TDto="InvoiceGetDto" ...
AllowEdit="true" EditOnRowClick="true"
RowClass="cursor-pointer"
SubmitLabel="Speichern" CancelLabel="Abbrechen" />
Explicit columns
Declare CCJNColumn children to control which columns appear, in which order, and how a cell is
rendered. As soon as one is declared, auto-detection is off and the declared columns are the table:
<CCJNDataGrid TDto="InvoiceGetDto" ... >
<Columns>
<CCJNColumn TDto="InvoiceGetDto" Property="Number" Title="Invoice" />
<CCJNColumn TDto="InvoiceGetDto" Title="Customer" SortBy="CustomerName"
Fields="@(new[] { "CustomerName", "CustomerEmail" })">
<CellTemplate Context="row">
<div>@row.CustomerName</div>
<MudText Typo="Typo.caption">@row.CustomerEmail</MudText>
</CellTemplate>
</CCJNColumn>
<CCJNColumn TDto="InvoiceGetDto" Property="Status">
<CellTemplate Context="row">
<MudChip T="string" Size="Size.Small">@row.Status</MudChip>
</CellTemplate>
</CCJNColumn>
</Columns>
</CCJNDataGrid>
| Column parameter | Type | Description |
|---|---|---|
Property |
string? |
DTO property shown; omit for a column built entirely by the template |
Title |
string? |
Header text; falls back to the property name split on capitals |
Sortable |
bool? |
Defaults to on for property-backed columns, off for template-only ones |
SortBy |
string? |
Property to sort by when it differs from Property |
Fields |
string[]? |
Further properties the template reads; requested but given no column |
CellTemplate |
RenderFragment<TDto>? |
Custom cell rendering, receiving the row item — name it via Context |
The key property is always requested, whether or not it has a column — edit and delete need it. The same goes for the fields of the edit form: it writes all of them back, so a value that was never loaded would be saved as its default and quietly overwrite what was there.
Column auto-detection
Columns are discovered automatically from the DTO via reflection. Scalar types are included; complex objects and collections are skipped.
Supported types: string, bool, char, byte, short, int, long, float, double, decimal, DateTime, DateTimeOffset, DateOnly, TimeOnly, TimeSpan, Guid, and all enum types (including nullable variants).
Property names are converted to headers by inserting spaces before each capital letter (FirstName → First Name). Override any header via ColumnHeaders.
Search
The search field queries all scalar columns simultaneously via a single GraphQL where: { or: [...] } clause:
- String columns —
contains(case-insensitive, server-side) - Numeric / Guid / bool columns —
eq(only applied when the search term is parseable as that type)
Search fires 500 ms after the last keystroke (debounce). The clear button resets immediately.
Example with all options
<CCJNDataGrid TDto="InvoiceGetDto"
GraphQLEndpoint="@Endpoint"
EntityName="invoice"
Title="Invoices"
Searchable="true"
Dense="true"
Striped="false"
Elevation="4"
BearerToken="@_token"
ExcludedProperties="@(new HashSet<string> { "Customer", "InternalNotes" })"
PageSizeOptions="@(new[] { 5, 10, 25 })"
ColumnHeaders="@(new Dictionary<string, string>
{
["Id"] = "Invoice ID",
["CustomerId"] = "Customer",
["Amount"] = "Amount (€)",
})" />
Programmatic reload
Expose a reference to the component and call ReloadTable() from code:
<CCJNDataGrid @ref="_grid" TDto="CustomerGetDto" ... />
@code {
private CCJNDataGrid<CustomerGetDto> _grid = default!;
private async Task Refresh() => await _grid.ReloadTable();
}
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- FluentValidation (>= 12.1.1)
- Microsoft.AspNetCore.Components.Web (>= 10.0.9)
- Microsoft.AspNetCore.Components.WebAssembly (>= 10.0.9)
- Microsoft.Extensions.Http (>= 10.0.9)
- MudBlazor (>= 9.5.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Rows and columns are now yours to shape. OnRowClick, RowClass, RowStyle, RowClassFunc and RowStyleFunc make the table clickable,
ViewFormContent opens a read-only detail dialog on a row click, and CCJNDataGridDialog gained ShowSubmit, ShowCancel and CloseLabel
so a dialog can carry nothing but a close button. Declaring CCJNColumn children switches auto-detection off and puts the set of
columns, their order and their cell rendering under your control, while HiddenProperties keeps values in the query that deserve
no column of their own.