Skip to content

Commit

Permalink
DataGrid: Dynamic data support through ExpandoObject (#5507)
Browse files Browse the repository at this point in the history
* Initial implementation for dynamic data support through ExpandoObject

* DynamicItem | DataGridAggregate | AutomaticallyGenerateColumns

* Demo DynamicDataPage | Format caption Pascal case

* Docs : Dynamic Data

* InvokeAsync( StateHasChanged )

* DataGrid | Fix merge

* Formating

* rephrase docs page

* release notes

---------

Co-authored-by: Mladen Macanovic <[email protected]>
  • Loading branch information
David-Moreira and stsrki authored Jun 12, 2024
1 parent 95d0ecd commit fba16c1
Show file tree
Hide file tree
Showing 21 changed files with 753 additions and 51 deletions.
4 changes: 4 additions & 0 deletions Demos/Blazorise.Demo/Components/SideMenu.razor
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@
</BarDropdownMenu>
</BarDropdown>

<BarDropdownItem To="tests/datagrid/dynamic">
Dynamic
</BarDropdownItem>

<BarDropdown>
<BarDropdownToggle>
Editing
Expand Down
129 changes: 129 additions & 0 deletions Demos/Blazorise.Demo/Pages/Tests/DataGrid/DynamicDataPage.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
@page "/tests/datagrid/dynamic"
@using System.Dynamic
<Row>
<Column>
<Card Margin="Margin.Is4.OnY">
<CardHeader>
<CardTitle>Datagrid: Dynamic</CardTitle>
</CardHeader>
<CardBody>
<Paragraph>
Our DataGrid allows you to bind data dynamically by supporting the use of the ExpandoObject.
</Paragraph>
</CardBody>
<CardBody>
<DataGrid TItem="ExpandoObject"
Data="inMemoryData"
Responsive
ShowPager
ShowPageSizes
PageSize="5"
Editable
EditMode="DataGridEditMode.Inline"
NewItemCreator="NewItemCreator">
<DataGridAggregates>
<DataGridAggregate Field="Email" Aggregate="DataGridAggregateType.Count">
<DisplayTemplate>
@($"Total emails: {context.Value}")
</DisplayTemplate>
</DataGridAggregate>
<DataGridAggregate Field="Salary" Aggregate="DataGridAggregateType.Sum" DisplayFormat="{0:C}" DisplayFormatProvider="@System.Globalization.CultureInfo.GetCultureInfo("fr-FR")" />
<DataGridAggregate Field="IsActive" Aggregate="DataGridAggregateType.TrueCount" />
<DataGridAggregate Field="Childrens" Aggregate="DataGridAggregateType.Sum" />
</DataGridAggregates>
<DataGridColumns>
<DataGridCommandColumn></DataGridCommandColumn>
<DataGridColumn Editable TextAlignment="TextAlignment.Center" Field="@nameof( Employee.Id )" Caption="#" Width="60px" />
<DataGridColumn Editable Field="FirstName" Caption="First Name">
</DataGridColumn>
<DataGridColumn Editable Field="LastName" Caption="Last Name" />
<DataGridColumn Editable Field="Email" Caption="Email" />
<DataGridColumn Editable Field="City" Caption="City">
<CaptionTemplate>
<Icon Name="IconName.City" /> @context.Caption
</CaptionTemplate>
</DataGridColumn>
<DataGridColumn Editable Field="Zip" Caption="Zip">
</DataGridColumn>
<DataGridDateColumn Field="DateOfBirth" DisplayFormat="{0:dd.MM.yyyy}" Caption="Date Of Birth" Editable />
<DataGridNumericColumn Field="Childrens" Caption="Childrens" ReverseSorting="true" Editable Filterable="false" />
<DataGridSelectColumn Field="Gender" Caption="Gender" Editable Data="EmployeeData.Genders" ValueField="(x) => ((Gender)x).Code" TextField="(x) => ((Gender)x).Description" />
<DataGridColumn Field="Salary" Caption="Salary" Editable Width="140px" DisplayFormat="{0:C}" DisplayFormatProvider="@System.Globalization.CultureInfo.GetCultureInfo("fr-FR")" TextAlignment="TextAlignment.End">
</DataGridColumn>
<DataGridCheckColumn Field="IsActive" Caption="Active" Editable Filterable="false">
<DisplayTemplate>
<Check TValue="bool" Checked='(context as dynamic).IsActive' Disabled ReadOnly />
</DisplayTemplate>
</DataGridCheckColumn>
</DataGridColumns>
</DataGrid>
</CardBody>
</Card>
</Column>
</Row>

<Row>
<Column>
<Card Margin="Margin.Is4.OnY">
<CardHeader>
<CardTitle>Datagrid: Dynamic Auto Generate Columns</CardTitle>
</CardHeader>
<CardBody>
<Paragraph>
Additionally the DataGrid can auto generate columns from your dynamic data.
</Paragraph>
</CardBody>
<CardBody>
<DataGrid TItem="ExpandoObject"
Data="inMemoryData"
Responsive
ShowPager
ShowPageSizes
PageSize="5"
Editable
EditMode="DataGridEditMode.Inline"
NewItemCreator="NewItemCreator" />
</CardBody>
</Card>
</Column>
</Row>

@code {

[Inject] EmployeeData EmployeeData { get; set; }

private List<ExpandoObject> inMemoryData;

protected override async Task OnInitializedAsync()
{
inMemoryData = new();
var data = ( await EmployeeData.GetDataAsync().ConfigureAwait( false ) ).Take( 25 );

foreach ( var item in data )
{

IDictionary<string, object> expando = new ExpandoObject();

foreach ( var property in typeof( Employee ).GetProperties() )
{
expando.Add( property.Name, property.GetValue( item ) );
}
inMemoryData.Add( (ExpandoObject)expando );
}


await base.OnInitializedAsync();
}

private ExpandoObject NewItemCreator()
{
IDictionary<string, object> expando = new ExpandoObject();

foreach ( var property in typeof( Employee ).GetProperties() )
{
expando.Add( property.Name, property.PropertyType.IsValueType ? Activator.CreateInstance( property.PropertyType ) : null );
}

return (ExpandoObject)expando;
}
}
1 change: 1 addition & 0 deletions Documentation/Blazorise.Docs/Layouts/DocsLayout.razor
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@
<BarDropdownItem To="docs/extensions/datagrid/binding-data/large-data">From an External Source</BarDropdownItem>
<BarDropdownItem To="docs/extensions/datagrid/binding-data/virtualize">Virtualize</BarDropdownItem>
<BarDropdownItem To="docs/extensions/datagrid/binding-data/observable">Observable Collections</BarDropdownItem>
<BarDropdownItem To="docs/extensions/datagrid/binding-data/dynamic">Dynamic</BarDropdownItem>
</BarDropdownMenu>
</BarDropdown>

Expand Down
141 changes: 139 additions & 2 deletions Documentation/Blazorise.Docs/Models/Snippets.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6769,7 +6769,6 @@ ShowPageSizes Editable>
</DataGrid>
@code {
public class Example
{
[Order( DisplayOrder = 1, EditOrder = 2 )]
Expand Down Expand Up @@ -6822,7 +6821,6 @@ public enum Status
new(){ FirstName = ""Jack"", LastName = ""Doe"", Gender = ""M"",Age = 22, Balance = 5000, Status = Status.Active, FieldToBeIgnored = ""76471dfe-2efd-4ec5-b192-82abc1b05c72"", DateOfBirth = new DateOnly(1990,09,10) },
new(){ FirstName = ""Jen"", LastName = ""Doe"",Gender = ""F"", Age = 20, Balance = 6000, Status = Status.Active, FieldToBeIgnored = ""be83a3c0-9636-4ebd-acca-08e6ffb5c469"", DateOfBirth = new DateOnly(2000,01,01) },
};
}";

public const string DataGridBatchEditExample = @"<Field>
Expand Down Expand Up @@ -7336,6 +7334,145 @@ protected override async Task OnInitializedAsync()
}
}";

public const string DataGridDynamicAutoGenerateExample = @"@using System.Dynamic
<DataGrid TItem=""ExpandoObject""
Data=""inMemoryData""
Responsive
ShowPager
ShowPageSizes
PageSize=""5""
Editable
EditMode=""DataGridEditMode.Inline""
NewItemCreator=""NewItemCreator"" />
@code {
[Inject] EmployeeData EmployeeData { get; set; }
private List<ExpandoObject> inMemoryData;
protected override async Task OnInitializedAsync()
{
inMemoryData = new();
var data = ( await EmployeeData.GetDataAsync().ConfigureAwait( false ) ).Take( 25 );
foreach ( var item in data )
{
IDictionary<string, object> expando = new ExpandoObject();
foreach ( var property in typeof( Employee ).GetProperties() )
{
expando.Add( property.Name, property.GetValue( item ) );
}
inMemoryData.Add( (ExpandoObject)expando );
}
await base.OnInitializedAsync();
}
private ExpandoObject NewItemCreator()
{
IDictionary<string, object> expando = new ExpandoObject();
foreach ( var property in typeof( Employee ).GetProperties() )
{
expando.Add( property.Name, property.PropertyType.IsValueType ? Activator.CreateInstance( property.PropertyType ) : null );
}
return (ExpandoObject)expando;
}
}";

public const string DataGridDynamicExample = @"@using System.Dynamic
<DataGrid TItem=""ExpandoObject""
Data=""inMemoryData""
Responsive
ShowPager
ShowPageSizes
PageSize=""5""
Editable
EditMode=""DataGridEditMode.Inline""
NewItemCreator=""NewItemCreator"">
<DataGridAggregates>
<DataGridAggregate Field=""Email"" Aggregate=""DataGridAggregateType.Count"">
<DisplayTemplate>
@($""Total emails: {context.Value}"")
</DisplayTemplate>
</DataGridAggregate>
<DataGridAggregate Field=""Salary"" Aggregate=""DataGridAggregateType.Sum"" DisplayFormat=""{0:C}"" DisplayFormatProvider=""@System.Globalization.CultureInfo.GetCultureInfo(""fr-FR"")"" />
<DataGridAggregate Field=""IsActive"" Aggregate=""DataGridAggregateType.TrueCount"" />
<DataGridAggregate Field=""Childrens"" Aggregate=""DataGridAggregateType.Sum"" />
</DataGridAggregates>
<DataGridColumns>
<DataGridCommandColumn></DataGridCommandColumn>
<DataGridColumn Editable TextAlignment=""TextAlignment.Center"" Field=""@nameof( Employee.Id )"" Caption=""#"" Width=""60px"" />
<DataGridColumn Editable Field=""FirstName"" Caption=""First Name"">
</DataGridColumn>
<DataGridColumn Editable Field=""LastName"" Caption=""Last Name"" />
<DataGridColumn Editable Field=""Email"" Caption=""Email"" />
<DataGridColumn Editable Field=""City"" Caption=""City"">
<CaptionTemplate>
<Icon Name=""IconName.City"" /> @context.Caption
</CaptionTemplate>
</DataGridColumn>
<DataGridColumn Editable Field=""Zip"" Caption=""Zip"">
</DataGridColumn>
<DataGridDateColumn Field=""DateOfBirth"" DisplayFormat=""{0:dd.MM.yyyy}"" Caption=""Date Of Birth"" Editable />
<DataGridNumericColumn Field=""Childrens"" Caption=""Childrens"" ReverseSorting=""true"" Editable Filterable=""false"" />
<DataGridSelectColumn Field=""Gender"" Caption=""Gender"" Editable Data=""EmployeeData.Genders"" ValueField=""(x) => ((Gender)x).Code"" TextField=""(x) => ((Gender)x).Description"" />
<DataGridColumn Field=""Salary"" Caption=""Salary"" Editable Width=""140px"" DisplayFormat=""{0:C}"" DisplayFormatProvider=""@System.Globalization.CultureInfo.GetCultureInfo(""fr-FR"")"" TextAlignment=""TextAlignment.End"">
</DataGridColumn>
<DataGridCheckColumn Field=""IsActive"" Caption=""Active"" Editable Filterable=""false"">
<DisplayTemplate>
<Check TValue=""bool"" Checked='(context as dynamic).IsActive' Disabled ReadOnly />
</DisplayTemplate>
</DataGridCheckColumn>
</DataGridColumns>
</DataGrid>
@code {
[Inject] EmployeeData EmployeeData { get; set; }
private List<ExpandoObject> inMemoryData;
protected override async Task OnInitializedAsync()
{
inMemoryData = new();
var data = ( await EmployeeData.GetDataAsync().ConfigureAwait( false ) ).Take( 25 );
foreach ( var item in data )
{
IDictionary<string, object> expando = new ExpandoObject();
foreach ( var property in typeof( Employee ).GetProperties() )
{
expando.Add( property.Name, property.GetValue( item ) );
}
inMemoryData.Add( (ExpandoObject)expando );
}
await base.OnInitializedAsync();
}
private ExpandoObject NewItemCreator()
{
IDictionary<string, object> expando = new ExpandoObject();
foreach ( var property in typeof( Employee ).GetProperties() )
{
expando.Add( property.Name, property.PropertyType.IsValueType ? Activator.CreateInstance( property.PropertyType ) : null );
}
return (ExpandoObject)expando;
}
}";

public const string DataGridEditModeExample = @"<Field>
<FieldLabel>
Edit Mode
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@page "/docs/extensions/datagrid/binding-data/dynamic"

<Seo Canonical="/docs/extensions/datagrid/dynamic" Title="Blazorise DataGrid Binding Dynamic data" Description="The DataGrid can bind to your dynamic data, allowing for greater flexibility." />

<DocsPageTitle Path="Extensions/DataGrid/Binding-Data/Dynamic">
Blazorise DataGrid: Dynamic Data Binding
</DocsPageTitle>

<DocsPageLead>
The Blazorise <Code>DataGrid</Code> component offers the ability to bind to dynamic data, providing enhanced flexibility for various applications.
</DocsPageLead>

<DocsPageSection>
<DocsPageSectionHeader Title="Basic Example">
To ensure the <Code>DataGrid</Code> correctly interprets the structure and type of your dynamic data during specific operations, such as creating new items or aggregating data, you need to define a representation within the <Code>NewItemCreator</Code> function. Below is an illustrative example.
</DocsPageSectionHeader>
<DocsPageSectionContent FullWidth>
<DataGridDynamicExample />
</DocsPageSectionContent>
<DocsPageSectionSource Code="DataGridDynamicExample" />
</DocsPageSection>

<DocsPageSection>
<DocsPageSectionHeader Title="Auto Generate Columns Example">
The Blazorise DataGrid also supports auto-generating columns based on the data structure. This feature simplifies the process of displaying data by automatically creating columns that match the properties of the bound data objects.
</DocsPageSectionHeader>
<DocsPageSectionContent FullWidth>
<DataGridDynamicAutoGenerateExample />
</DocsPageSectionContent>
<DocsPageSectionSource Code="DataGridDynamicAutoGenerateExample" />
</DocsPageSection>

<DocsPageApi>
<DocsPageApiItem Url="docs/extensions/datagrid/api" Name="<DataGrid />" />
</DocsPageApi>
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
</pre></div>
<div class="csharp"><pre>
<span class="atSign">&#64;</span>code {

<span class="keyword">public</span> <span class="keyword">class</span> Example
{
[Order( DisplayOrder = <span class="number">1</span>, EditOrder = <span class="number">2</span> )]
Expand Down Expand Up @@ -65,7 +64,6 @@
<span class="keyword">new</span>(){ FirstName = <span class="string">&quot;Jack&quot;</span>, LastName = <span class="string">&quot;Doe&quot;</span>, Gender = <span class="string">&quot;M&quot;</span>,Age = <span class="number">22</span>, Balance = <span class="number">5000</span>, Status = Status.Active, FieldToBeIgnored = <span class="string">&quot;76471dfe-2efd-4ec5-b192-82abc1b05c72&quot;</span>, DateOfBirth = <span class="keyword">new</span> DateOnly(<span class="number">1990</span>,<span class="number">09</span>,<span class="number">10</span>) },
<span class="keyword">new</span>(){ FirstName = <span class="string">&quot;Jen&quot;</span>, LastName = <span class="string">&quot;Doe&quot;</span>,Gender = <span class="string">&quot;F&quot;</span>, Age = <span class="number">20</span>, Balance = <span class="number">6000</span>, Status = Status.Active, FieldToBeIgnored = <span class="string">&quot;be83a3c0-9636-4ebd-acca-08e6ffb5c469&quot;</span>, DateOfBirth = <span class="keyword">new</span> DateOnly(<span class="number">2000</span>,<span class="number">01</span>,<span class="number">01</span>) },
};

}
</pre></div>
</div>
Loading

0 comments on commit fba16c1

Please sign in to comment.