Skip to content

Commit

Permalink
DataGrid: add State API (#5212)
Browse files Browse the repository at this point in the history
* DataGrid State | Ongoing

* Demo | StateManagement page | Add docs & Item states

* remove leftover from testing

* State | Get sort & filter info | Reset Sort & Filter if no info is provided

* Docs | Add StateManagement page

* DataGrid docs | API methods for LoadState And GetState

* Release notes for DataGrid State Management

* ResetFiltering | Columns NullEmpty Check

* SetNewState | SetEditState

* Move States to their own files

* Using directives remove lines

* Formating and comments

---------

Co-authored-by: Mladen Macanovic <[email protected]>
  • Loading branch information
David-Moreira and stsrki authored Jan 25, 2024
1 parent 741eee7 commit 7b705df
Show file tree
Hide file tree
Showing 18 changed files with 899 additions and 28 deletions.
1 change: 1 addition & 0 deletions Build/Blazorise.Demo.props
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<PackageReference Include="FluentValidation" Version="11.2.0" />
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.2.0" />
<PackageReference Include="Flurl.Http" Version="4.0.0-pre2" />
<PackageReference Include="Blazored.LocalStorage" Version="4.4.0" />
</ItemGroup>

</Project>
4 changes: 4 additions & 0 deletions Demos/Blazorise.Demo/Components/SideMenu.razor
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@
</BarDropdownMenu>
</BarDropdown>

<BarDropdownItem To="tests/datagrid/state">
State Management
</BarDropdownItem>

<BarDropdownItem To="tests/datagrid/validations">
Validations
</BarDropdownItem>
Expand Down
3 changes: 3 additions & 0 deletions Demos/Blazorise.Demo/Config.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#region Using directives
using Blazored.LocalStorage;
using Blazorise.FluentValidation;
using Blazorise.LoadingIndicator;
using Blazorise.RichTextEdit;
Expand Down Expand Up @@ -26,6 +27,8 @@ public static IServiceCollection SetupDemoServices( this IServiceCollection serv
.AddLoadingIndicator()
.AddBlazoriseFluentValidation();

services.AddBlazoredLocalStorage();

services.AddValidatorsFromAssembly( typeof( App ).Assembly );

services.AddMemoryCache();
Expand Down
126 changes: 126 additions & 0 deletions Demos/Blazorise.Demo/Pages/Tests/DataGrid/StateManagementPage.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
@page "/tests/datagrid/state"

<Row>
<Column>
<Card Margin="Margin.Is4.OnY">
<CardHeader>
<CardTitle>Datagrid: State Management</CardTitle>
</CardHeader>
<CardBody>
<Paragraph>
Our DataGrid allows you to save and load state.
You can use the <code>LoadState</code> and <code>GetState</code> methods to load and get the DataGrid state.
</Paragraph>
<Paragraph>
In the following example,
<UnorderedList>
<UnorderedListItem>we are using the <code>LoadState</code> method to load the DataGrid state from the LocalStorage if available.</UnorderedListItem>
<UnorderedListItem>We are using the <code>GetState</code> method to save the DataGrid state to the LocalStorage in order to load at a later date.</UnorderedListItem>
<UnorderedListItem>The page checks the LocalStorage on first render and loads the saved state if available.</UnorderedListItem>
</UnorderedList>
</Paragraph>
<Paragraph>
<Button Color="Color.Primary" Clicked="LoadState">Load State</Button>
<Button Color="Color.Success" Clicked="SaveState">Save State</Button>
<Button Color="Color.Light" Clicked="ResetState">Reset State</Button>
</Paragraph>
</CardBody>
<CardBody>
<DataGrid @ref="dataGridRef"
TItem="Employee"
Data="inMemoryData"
Responsive
Editable
Filterable
ShowPager
ShowPageSizes>
<DataGridColumns>
<DataGridColumn TextAlignment="TextAlignment.Center" TItem="Employee" Field="@nameof( Employee.Id )" Caption="#" Width="60px" />
<DataGridColumn TItem="Employee" Field="@nameof( Employee.FirstName )" Caption="First Name">
</DataGridColumn>
<DataGridColumn TItem="Employee" Field="@nameof( Employee.LastName )" Caption="Last Name" />
<DataGridColumn TItem="Employee" Field="@nameof( Employee.Email )" Caption="Email" />
<DataGridColumn TItem="Employee" Field="@nameof( Employee.City )" Caption="City">
<CaptionTemplate>
<Icon Name="IconName.City" /> @context.Caption
</CaptionTemplate>
</DataGridColumn>
<DataGridColumn TItem="Employee" Field="@nameof( Employee.Zip )" Caption="Zip">
</DataGridColumn>
<DataGridDateColumn TItem="Employee" Field="@nameof( Employee.DateOfBirth )" DisplayFormat="{0:dd.MM.yyyy}" Caption="Date Of Birth" Editable />
<DataGridNumericColumn TItem="Employee" Field="@nameof( Employee.Childrens )" Caption="Childrens" ReverseSorting="true" Editable Filterable="false" />
<DataGridSelectColumn TItem="Employee" Field="@nameof( Employee.Gender )" Caption="Gender" Editable Data="EmployeeData.Genders" ValueField="(x) => ((Gender)x).Code" TextField="(x) => ((Gender)x).Description" />
<DataGridColumn TItem="Employee" Field="@nameof( Employee.Salary )" Caption="Salary" Editable Width="140px" DisplayFormat="{0:C}" DisplayFormatProvider="@System.Globalization.CultureInfo.GetCultureInfo("fr-FR")" TextAlignment="TextAlignment.End">
</DataGridColumn>
<DataGridCheckColumn TItem="Employee" Field="@nameof(Employee.IsActive)" Caption="Active" Editable Filterable="false">
<DisplayTemplate>
<Check TValue="bool" Checked="context.IsActive" Disabled ReadOnly />
</DisplayTemplate>
</DataGridCheckColumn>
</DataGridColumns>
</DataGrid>
</CardBody>
</Card>
</Column>
</Row>

@code {
[Inject] Blazored.LocalStorage.ILocalStorageService LocalStorage { get; set; }
[Inject] EmployeeData EmployeeData { get; set; }

private const string STORAGE_KEY = "__DATAGRID_STATE__";
private DataGrid<Employee> dataGridRef;
private IEnumerable<Employee> inMemoryData;

protected override async Task OnInitializedAsync()
{
inMemoryData = ( await EmployeeData.GetDataAsync().ConfigureAwait( false ) ).Take( 25 );
await base.OnInitializedAsync();
}

protected async override Task OnAfterRenderAsync( bool firstRender )
{
if ( firstRender )
{
await LoadState();
}
await base.OnAfterRenderAsync( firstRender );
}

private async Task ResetState()
{
await LocalStorage.RemoveItemAsync( STORAGE_KEY );
var state = new DataGridState<Employee>()
{
CurrentPage = 1,
PageSize = 10,
};
await dataGridRef.LoadState( state );
}

private async Task LoadState()
{
var stateFromLocalStorage = await LocalStorage.GetItemAsync<DataGridState<Employee>>( STORAGE_KEY );

if ( stateFromLocalStorage is not null )
{
//It is of note that we must make sure the reference is contained in the DataGrid Data collection.
if ( stateFromLocalStorage.SelectedRow is not null )
{
stateFromLocalStorage.SelectedRow = inMemoryData.FirstOrDefault( x => x.Id == stateFromLocalStorage.SelectedRow.Id );
}
if ( stateFromLocalStorage.EditItem is not null )
{
stateFromLocalStorage.EditItem = inMemoryData.FirstOrDefault( x => x.Id == stateFromLocalStorage.EditItem.Id );
}
await dataGridRef.LoadState( stateFromLocalStorage );
return;
}
}

private async Task SaveState()
{
var state = await dataGridRef.GetState();
await LocalStorage.SetItemAsync( STORAGE_KEY, state );
}
}
4 changes: 3 additions & 1 deletion Documentation/Blazorise.Docs.Server/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.IO.Compression;
using System.Linq;
using Blazored.LocalStorage;
using Blazorise.Bootstrap5;
using Blazorise.Docs.Core;
using Blazorise.Docs.Models;
Expand All @@ -17,7 +18,6 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;

namespace Blazorise.Docs.Server;

Expand Down Expand Up @@ -54,6 +54,8 @@ public void ConfigureServices( IServiceCollection services )

services.AddValidatorsFromAssembly( typeof( App ).Assembly );

services.AddBlazoredLocalStorage();

services.AddMemoryCache();
services.AddScoped<Shared.Data.EmployeeData>();
services.AddScoped<Shared.Data.CountryData>();
Expand Down
1 change: 1 addition & 0 deletions Documentation/Blazorise.Docs/Blazorise.Docs.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<ProjectReference Include="..\..\Source\Extensions\Blazorise.Splitter\Blazorise.Splitter.csproj" />
<PackageReference Include="MailKit" Version="3.1.1" />
<PackageReference Include="MimeKit" Version="3.1.1" />
<PackageReference Include="Blazored.LocalStorage" Version="4.4.0" />
</ItemGroup>

<!--Binary path for the code generator-->
Expand Down
1 change: 1 addition & 0 deletions Documentation/Blazorise.Docs/Layouts/DocsLayout.razor
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@
<BarDropdownItem To="docs/extensions/datagrid/features/context-menu">Context Menu</BarDropdownItem>
<BarDropdownItem To="docs/extensions/datagrid/features/grouping">Grouping</BarDropdownItem>
<BarDropdownItem To="docs/extensions/datagrid/features/header-group">Header Group</BarDropdownItem>
<BarDropdownItem To="docs/extensions/datagrid/features/state-management">State Management</BarDropdownItem>
</BarDropdownMenu>
</BarDropdown>

Expand Down
104 changes: 104 additions & 0 deletions Documentation/Blazorise.Docs/Models/Snippets.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7955,6 +7955,110 @@ protected override async Task OnInitializedAsync()
}
}";

public const string DataGridStateManagementExample = @"<Paragraph>
<Button Color=""Color.Primary"" Clicked=""LoadState"">Load State</Button>
<Button Color=""Color.Success"" Clicked=""SaveState"">Save State</Button>
<Button Color=""Color.Light"" Clicked=""ResetState"">Reset State</Button>
</Paragraph>
<DataGrid @ref=""dataGridRef""
TItem=""Employee""
Data=""inMemoryData""
Responsive
Editable
Filterable
ShowPager
ShowPageSizes>
<DataGridColumns>
<DataGridColumn TextAlignment=""TextAlignment.Center"" TItem=""Employee"" Field=""@nameof( Employee.Id )"" Caption=""#"" Width=""60px"" />
<DataGridColumn TItem=""Employee"" Field=""@nameof( Employee.FirstName )"" Caption=""First Name"">
</DataGridColumn>
<DataGridColumn TItem=""Employee"" Field=""@nameof( Employee.LastName )"" Caption=""Last Name"" />
<DataGridColumn TItem=""Employee"" Field=""@nameof( Employee.Email )"" Caption=""Email"" />
<DataGridColumn TItem=""Employee"" Field=""@nameof( Employee.City )"" Caption=""City"">
<CaptionTemplate>
<Icon Name=""IconName.City"" /> @context.Caption
</CaptionTemplate>
</DataGridColumn>
<DataGridColumn TItem=""Employee"" Field=""@nameof( Employee.Zip )"" Caption=""Zip"">
</DataGridColumn>
<DataGridDateColumn TItem=""Employee"" Field=""@nameof( Employee.DateOfBirth )"" DisplayFormat=""{0:dd.MM.yyyy}"" Caption=""Date Of Birth"" Editable />
<DataGridNumericColumn TItem=""Employee"" Field=""@nameof( Employee.Childrens )"" Caption=""Childrens"" ReverseSorting=""true"" Editable Filterable=""false"" />
<DataGridSelectColumn TItem=""Employee"" Field=""@nameof( Employee.Gender )"" Caption=""Gender"" Editable Data=""EmployeeData.Genders"" ValueField=""(x) => ((Gender)x).Code"" TextField=""(x) => ((Gender)x).Description"" />
<DataGridColumn TItem=""Employee"" Field=""@nameof( Employee.Salary )"" Caption=""Salary"" Editable Width=""140px"" DisplayFormat=""{0:C}"" DisplayFormatProvider=""@System.Globalization.CultureInfo.GetCultureInfo(""fr-FR"")"" TextAlignment=""TextAlignment.End"">
</DataGridColumn>
<DataGridCheckColumn TItem=""Employee"" Field=""@nameof(Employee.IsActive)"" Caption=""Active"" Editable Filterable=""false"">
<DisplayTemplate>
<Check TValue=""bool"" Checked=""context.IsActive"" Disabled ReadOnly />
</DisplayTemplate>
</DataGridCheckColumn>
</DataGridColumns>
</DataGrid>
@code {
[Inject] Blazored.LocalStorage.ILocalStorageService LocalStorage { get; set; }
[Inject] EmployeeData EmployeeData { get; set; }
private const string STORAGE_KEY = ""__DATAGRID_STATE__"";
private DataGrid<Employee> dataGridRef;
private IEnumerable<Employee> inMemoryData;
protected override async Task OnInitializedAsync()
{
inMemoryData = ( await EmployeeData.GetDataAsync().ConfigureAwait( false ) ).Take( 25 );
await base.OnInitializedAsync();
}
protected async override Task OnAfterRenderAsync( bool firstRender )
{
if ( firstRender )
{
await LoadState();
}
await base.OnAfterRenderAsync( firstRender );
}
private async Task ResetState()
{
await LocalStorage.RemoveItemAsync( STORAGE_KEY );
var state = new DataGridState<Employee>()
{
CurrentPage = 1,
PageSize = 10,
};
await dataGridRef.LoadState( state );
}
private async Task LoadState()
{
var stateFromLocalStorage = await LocalStorage.GetItemAsync<DataGridState<Employee>>( STORAGE_KEY );
if ( stateFromLocalStorage is not null )
{
//It is of note that we must make sure the reference is contained in the DataGrid Data collection.
if ( stateFromLocalStorage.SelectedRow is not null )
{
stateFromLocalStorage.SelectedRow = inMemoryData.FirstOrDefault( x => x.Id == stateFromLocalStorage.SelectedRow.Id );
}
if ( stateFromLocalStorage.EditItem is not null )
{
stateFromLocalStorage.EditItem = inMemoryData.FirstOrDefault( x => x.Id == stateFromLocalStorage.EditItem.Id );
}
await dataGridRef.LoadState( stateFromLocalStorage );
return;
}
}
private async Task SaveState()
{
var state = await dataGridRef.GetState();
await LocalStorage.SetItemAsync( STORAGE_KEY, state );
}
}";

public const string DataGridUpdateCellExample = @"<DataGrid TItem=""Employee""
Data=""@employeeList""
Editable
Expand Down
Loading

0 comments on commit 7b705df

Please sign in to comment.