Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DataGrid: add ExpressionCompiler utility #5872

Merged
merged 9 commits into from
Dec 19, 2024
9 changes: 4 additions & 5 deletions Demos/Blazorise.Demo/Pages/Tests/DataGrid/DataGridPage.razor
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,8 @@
RowUpdating="@OnRowUpdating"
RowRemoving="@OnRowRemoving"
UseInternalEditing="false"
@bind-SelectedRow="@selectedEmployee"
@bind-SelectedRows="@selectedEmployees"
@bind-SelectedRow="@selectedEmployee"
@bind-SelectedRows="@selectedEmployees"
NewItemDefaultSetter="@OnEmployeeNewItemDefaultSetter"
DetailRowTrigger="@((e)=> e.Item.Salaries?.Count > 0 && e.Item.Id == selectedEmployee?.Id)"
Striped
Expand All @@ -254,7 +254,7 @@
Responsive
ValidationsSummaryLabel="Following error occurs..."
CustomFilter="@OnCustomFilter"
PageSize="5"
@bind-PageSize="pageSize"
CurrentPage="currentPage"
PageChanged="(e) => currentPage = e.Page"
FilteredDataChanged="@OnFilteredDataChanged"
Expand Down Expand Up @@ -354,8 +354,7 @@
</SortDirectionTemplate>
</DataGridColumn>
<DataGridDateColumn Field="@nameof( Employee.DateOfBirth )" DisplayFormat="{0:dd.MM.yyyy}" Caption="Date Of Birth" Editable />
<DataGridNumericColumn Field="@nameof( Employee.Childrens )" Caption="Childrens" Editable Filterable="false" />

<DataGridNumericColumn Field="@nameof( Employee.Childrens )" Caption="Childrens" Editable Filterable/>
<DataGridSelectColumn TItem="Employee" Field="@nameof( Employee.Gender )" Caption="Gender"
CustomFilter="@OnGenderCustomFilter"
Editable Data="EmployeeData.Genders" ValueField="(x) => ((Gender)x).Code" TextField="(x) => ((Gender)x).Description" />
Expand Down
86 changes: 9 additions & 77 deletions Demos/Blazorise.Demo/Pages/Tests/DataGrid/DataGridPage.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public partial class DataGridPage

private DataGrid<Employee> dataGrid;
public int currentPage { get; set; } = 1;
public int pageSize { get; set; } = 5;

private bool editable = true;
private bool fixedHeader = false;
Expand Down Expand Up @@ -203,73 +204,22 @@ private async Task OnReadData( DataGridReadDataEventArgs<Employee> e )
{
if ( !e.CancellationToken.IsCancellationRequested )
{
List<Employee> response = null;
var query = dataModels.AsQueryable().ApplyDataGridSort( e ).ApplyDataGridSearch( e );

var filteredData = await FilterData( e.Columns );
if ( dataGrid.CustomFilter is not null )
query = query.Where( item => item != null && dataGrid.CustomFilter( item ) );

// this can be call to anything, in this case we're calling a fictional api
if ( e.ReadDataMode is DataGridReadDataMode.Virtualize )
response = filteredData.Skip( e.VirtualizeOffset ).Take( e.VirtualizeCount ).ToList();
else if ( e.ReadDataMode is DataGridReadDataMode.Paging )
response = filteredData.Skip( ( e.Page - 1 ) * e.PageSize ).Take( e.PageSize ).ToList();
else
throw new Exception( "Unhandled ReadDataMode" );
var response = new List<Employee>();
response = query.ApplyDataGridPaging( e ).ToList();

await Task.Delay( random.Next( 100 ) );
if ( !e.CancellationToken.IsCancellationRequested )
{
totalEmployees = filteredData.Count;
employeeList = new List<Employee>( response ); // an actual data for the current page
}
}
}

/// <summary>
/// Simple demo purpose example filter
/// </summary>
/// <param name="dataGridColumns"></param>
/// <returns></returns>
public Task<List<Employee>> FilterData( IEnumerable<DataGridColumnInfo> dataGridColumns )
{
var filteredData = dataModels.ToList();
var sortByColumns = dataGridColumns.Where( x => x.SortDirection != SortDirection.Default );
var firstSort = true;
if ( sortByColumns?.Any() ?? false )
{
IOrderedEnumerable<Employee> sortedCols = null;
foreach ( var sortByColumn in sortByColumns.OrderBy( x => x.SortIndex ) )
if ( !e.CancellationToken.IsCancellationRequested )
{
var valueGetter = FunctionCompiler.CreateValueGetter<Employee>( sortByColumn.SortField );

if ( firstSort )
{
if ( sortByColumn.SortDirection == SortDirection.Ascending )
sortedCols = dataModels.OrderBy( x => valueGetter( x ) );
else
sortedCols = dataModels.OrderByDescending( x => valueGetter( x ) );

firstSort = false;
}
else
{
if ( sortByColumn.SortDirection == SortDirection.Ascending )
sortedCols = sortedCols.ThenBy( x => valueGetter( x ) );
else
sortedCols = sortedCols.ThenByDescending( x => valueGetter( x ) );
}
totalEmployees = query.Count();
employeeList = response;
}
filteredData = sortedCols.ToList();
}

if ( dataGrid.CustomFilter != null )
filteredData = filteredData.Where( item => item != null && dataGrid.CustomFilter( item ) ).ToList();

foreach ( var column in dataGridColumns.Where( x => !string.IsNullOrWhiteSpace( x.SearchValue?.ToString() ) ) )
{
var valueGetter = FunctionCompiler.CreateValueGetter<Employee>( column.Field );
filteredData = filteredData.Where( x => valueGetter( x )?.ToString().IndexOf( column.SearchValue.ToString(), StringComparison.OrdinalIgnoreCase ) >= 0 ).ToList();
}
return Task.FromResult( filteredData );
}

private Task Reset()
Expand Down Expand Up @@ -301,23 +251,5 @@ private void OnSortChanged( DataGridSortChangedEventArgs eventArgs )
Console.WriteLine( $"Sort changed > Field: {eventArgs.ColumnFieldName}{sort}; Direction: {eventArgs.SortDirection};" );
}

private string TitleFromGender( string gender )
{
return gender switch
{
"M" => "Mr.",
"F" => "Mrs.",
_ => string.Empty,
};
}

private string TitleToName( string title, object name )
{
if ( string.IsNullOrEmpty( title ) )
return $"{name}";

return $"{title} {name}";
}

#endregion
}
65 changes: 65 additions & 0 deletions Documentation/Blazorise.Docs/Models/Snippets.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8501,6 +8501,71 @@ private async Task OnReadData( DataGridReadDataEventArgs<Employee> e )
}
}";

public const string DataGridLargeDataExpressionCompilerExample = @"@using Blazorise.DataGrid.Extensions;
@using Blazorise.DataGrid.Utils

<DataGrid @ref=dataGridRef
TItem=""Employee""
Data=""@employeeList""
ReadData=""@OnReadData""
TotalItems=""@totalEmployees""
PageSize=""10""
ShowPager
Responsive
Filterable
FilterMode=""DataGridFilterMode.Menu"">
<DataGridCommandColumn />
<DataGridColumn Field=""@nameof(Employee.FirstName)"" Caption=""First Name"" Editable />
<DataGridColumn Field=""@nameof(Employee.LastName)"" Caption=""Last Name"" Editable />
<DataGridSelectColumn TItem=""Employee"" Field=""@nameof( Employee.Gender )"" Caption=""Gender"" Editable Data=""EmployeeData.Genders"" ValueField=""(x) => ((Gender)x).Code"" TextField=""(x) => ((Gender)x).Description"" />
<DataGridNumericColumn Field=""@nameof( Employee.Childrens )"" Caption=""Childrens"" Editable />
<DataGridDateColumn Field=""@nameof( Employee.DateOfBirth )"" DisplayFormat=""{0:dd.MM.yyyy}"" Caption=""Date Of Birth"" Editable />
</DataGrid>

@code {
[Inject] public EmployeeData EmployeeData { get; set; }
private DataGrid<Employee> dataGridRef;
private List<Employee> employeeListSource;
private List<Employee> employeeList;

protected override async Task OnInitializedAsync()
{
employeeListSource = await EmployeeData.GetDataAsync();
await base.OnInitializedAsync();
}

private int totalEmployees;

private async Task OnReadData(DataGridReadDataEventArgs<Employee> e)
{

if (!e.CancellationToken.IsCancellationRequested)
{
var query = employeeListSource.AsQueryable().ApplyDataGridSort(e.Columns).ApplyDataGridSearch(e.Columns);

if (dataGridRef.CustomFilter is not null)
query = query.Where(item => item != null && dataGridRef.CustomFilter(item));

var response = new List<Employee>();

if (e.ReadDataMode is DataGridReadDataMode.Virtualize)
response = query.ApplyDataGridPaging(e.VirtualizeOffset + 1, e.VirtualizeCount).ToList();
else if (e.ReadDataMode is DataGridReadDataMode.Paging)
response = query.ApplyDataGridPaging(e.Page, e.PageSize).ToList();
else
throw new Exception(""Unhandled ReadDataMode"");

await Task.Delay(Random.Shared.Next(100));

if (!e.CancellationToken.IsCancellationRequested)
{
totalEmployees = query.Count();
employeeList = response;
}
}
}
}";

public const string DataGridLoadingEmptyTemplateExample = @"<DataGrid @ref=""datagridRef""
TItem=""Employee""
Data=""@employeeList""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@
<DocsPageSectionSource Code="DataGridLargeDataExample" />
</DocsPageSection>

<DocsPageSection>
<DocsPageSectionHeader Title="Expression Compiler">
<Paragraph>
This utility enables you to compile expressions and use them in conjunction with the DataGrid <Code>ReadData()</Code> to build an <Code>IQueryable</Code> for querying your data.
</Paragraph>
<Paragraph>
The <Code>ExpressionCompiler</Code> can work alongside the <Code>DataGridColumnInfo</Code> collection provided by <Code>ReadData</Code> to create expression-based queries (<Code>IQueryables</Code>) that can be utilized in ORMs like Entity Framework.
</Paragraph>
</DocsPageSectionHeader>
<DocsPageSectionContent FullWidth Outlined>
<DataGridLargeDataExpressionCompilerExample />
</DocsPageSectionContent>
<DocsPageSectionSource Code="DataGridLargeDataExpressionCompilerExample" />
</DocsPageSection>

<DocsPageApi>
<DocsPageApiItem Url="docs/extensions/datagrid/api" Name="<DataGrid />" />
</DocsPageApi>
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<div class="blazorise-codeblock">
<div class="html"><pre>
<span class="atSign">&#64;</span>using Blazorise.DataGrid.Extensions;
<span class="atSign">&#64;</span>using Blazorise.DataGrid.Utils

<span class="htmlTagDelimiter">&lt;</span><span class="htmlElementName">DataGrid</span> <span class="htmlAttributeName"><span class="atSign">&#64;</span>ref</span><span class="htmlOperator">=</span><span class="htmlAttributeValue">dataGridRef</span>
<span class="htmlAttributeName">TItem</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue">Employee</span><span class="quot">&quot;</span>
<span class="htmlAttributeName">Data</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="sharpVariable"><span class="atSign">&#64;</span>employeeList</span><span class="quot">&quot;</span>
<span class="htmlAttributeName">ReadData</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="sharpVariable"><span class="atSign">&#64;</span>OnReadData</span><span class="quot">&quot;</span>
<span class="htmlAttributeName">TotalItems</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="sharpVariable"><span class="atSign">&#64;</span>totalEmployees</span><span class="quot">&quot;</span>
<span class="htmlAttributeName">PageSize</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue">10</span><span class="quot">&quot;</span>
<span class="htmlAttributeName">ShowPager</span>
<span class="htmlAttributeName">Responsive</span>
<span class="htmlAttributeName">Filterable</span>
<span class="htmlAttributeName">FilterMode</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="enum">DataGridFilterMode</span><span class="enumValue">.Menu</span><span class="quot">&quot;</span><span class="htmlTagDelimiter">&gt;</span>
<span class="htmlTagDelimiter">&lt;</span><span class="htmlElementName">DataGridCommandColumn</span> <span class="htmlTagDelimiter">/&gt;</span>
<span class="htmlTagDelimiter">&lt;</span><span class="htmlElementName">DataGridColumn</span> <span class="htmlAttributeName">Field</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue"><span class="atSign">&#64;</span>nameof(Employee.FirstName)</span><span class="quot">&quot;</span> <span class="htmlAttributeName">Caption</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue">First Name</span><span class="quot">&quot;</span> <span class="htmlAttributeName">Editable</span> <span class="htmlTagDelimiter">/&gt;</span>
<span class="htmlTagDelimiter">&lt;</span><span class="htmlElementName">DataGridColumn</span> <span class="htmlAttributeName">Field</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue"><span class="atSign">&#64;</span>nameof(Employee.LastName)</span><span class="quot">&quot;</span> <span class="htmlAttributeName">Caption</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue">Last Name</span><span class="quot">&quot;</span> <span class="htmlAttributeName">Editable</span> <span class="htmlTagDelimiter">/&gt;</span>
<span class="htmlTagDelimiter">&lt;</span><span class="htmlElementName">DataGridSelectColumn</span> <span class="htmlAttributeName">TItem</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue">Employee</span><span class="quot">&quot;</span> <span class="htmlAttributeName">Field</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue"><span class="atSign">&#64;</span>nameof( Employee.Gender )</span><span class="quot">&quot;</span> <span class="htmlAttributeName">Caption</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue">Gender</span><span class="quot">&quot;</span> <span class="htmlAttributeName">Editable</span> <span class="htmlAttributeName">Data</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="enum">EmployeeData</span><span class="enumValue">.Genders</span><span class="quot">&quot;</span> <span class="htmlAttributeName">ValueField</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue">(x) =&gt; ((Gender)x).Code</span><span class="quot">&quot;</span> <span class="htmlAttributeName">TextField</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue">(x) =&gt; ((Gender)x).Description</span><span class="quot">&quot;</span> <span class="htmlTagDelimiter">/&gt;</span>
<span class="htmlTagDelimiter">&lt;</span><span class="htmlElementName">DataGridNumericColumn</span> <span class="htmlAttributeName">Field</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue"><span class="atSign">&#64;</span>nameof( Employee.Childrens )</span><span class="quot">&quot;</span> <span class="htmlAttributeName">Caption</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue">Childrens</span><span class="quot">&quot;</span> <span class="htmlAttributeName">Editable</span> <span class="htmlTagDelimiter">/&gt;</span>
<span class="htmlTagDelimiter">&lt;</span><span class="htmlElementName">DataGridDateColumn</span> <span class="htmlAttributeName">Field</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue"><span class="atSign">&#64;</span>nameof( Employee.DateOfBirth )</span><span class="quot">&quot;</span> <span class="htmlAttributeName">DisplayFormat</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue">{0:dd.MM.yyyy}</span><span class="quot">&quot;</span> <span class="htmlAttributeName">Caption</span><span class="htmlOperator">=</span><span class="quot">&quot;</span><span class="htmlAttributeValue">Date Of Birth</span><span class="quot">&quot;</span> <span class="htmlAttributeName">Editable</span> <span class="htmlTagDelimiter">/&gt;</span>
<span class="htmlTagDelimiter">&lt;/</span><span class="htmlElementName">DataGrid</span><span class="htmlTagDelimiter">&gt;</span>
</pre></div>
<div class="csharp"><pre>
<span class="atSign">&#64;</span>code {
[Inject] <span class="keyword">public</span> EmployeeData EmployeeData { <span class="keyword">get</span>; <span class="keyword">set</span>; }
<span class="keyword">private</span> DataGrid&lt;Employee&gt; dataGridRef;
<span class="keyword">private</span> List&lt;Employee&gt; employeeListSource;
<span class="keyword">private</span> List&lt;Employee&gt; employeeList;

<span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">async</span> Task OnInitializedAsync()
{
employeeListSource = <span class="keyword">await</span> EmployeeData.GetDataAsync();
<span class="keyword">await</span> <span class="keyword">base</span>.OnInitializedAsync();
}

<span class="keyword">private</span> <span class="keyword">int</span> totalEmployees;

<span class="keyword">private</span> <span class="keyword">async</span> Task OnReadData(DataGridReadDataEventArgs&lt;Employee&gt; e)
{

<span class="keyword">if</span> (!e.CancellationToken.IsCancellationRequested)
{
<span class="keyword">var</span> query = employeeListSource.AsQueryable().ApplyDataGridSort(e.Columns).ApplyDataGridSearch(e.Columns);

<span class="keyword">if</span> (dataGridRef.CustomFilter <span class="keyword">is</span> not <span class="keyword">null</span>)
query = query.Where(item =&gt; item != <span class="keyword">null</span> &amp;&amp; dataGridRef.CustomFilter(item));

<span class="keyword">var</span> response = <span class="keyword">new</span> List&lt;Employee&gt;();

<span class="keyword">if</span> (e.ReadDataMode <span class="keyword">is</span> DataGridReadDataMode.Virtualize)
response = query.ApplyDataGridPaging(e.VirtualizeOffset + <span class="number">1</span>, e.VirtualizeCount).ToList();
<span class="keyword">else</span> <span class="keyword">if</span> (e.ReadDataMode <span class="keyword">is</span> DataGridReadDataMode.Paging)
response = query.ApplyDataGridPaging(e.Page, e.PageSize).ToList();
<span class="keyword">else</span>
<span class="keyword">throw</span> <span class="keyword">new</span> Exception(<span class="string">&quot;Unhandled ReadDataMode&quot;</span>);

<span class="keyword">await</span> Task.Delay(Random.Shared.Next(<span class="number">100</span>));

<span class="keyword">if</span> (!e.CancellationToken.IsCancellationRequested)
{
totalEmployees = query.Count();
employeeList = response;
}
}
}
}
</pre></div>
</div>
Loading
Loading