Skip to content

Commit 61622dd

Browse files
Save Grid state in MS ProtectedBrowserStorage (#126)
* feat(grid): state in MS ProtectedBrowserStorage * Update grid/save-state-in-ProtectedBrowserStorage/readme.md Co-authored-by: Dimo Dimov <961014+dimodi@users.noreply.github.com> * Update grid/save-state-in-ProtectedBrowserStorage/readme.md Co-authored-by: Dimo Dimov <961014+dimodi@users.noreply.github.com> * Update grid/save-state-in-ProtectedBrowserStorage/readme.md Co-authored-by: Dimo Dimov <961014+dimodi@users.noreply.github.com> * Update grid/save-state-in-ProtectedBrowserStorage/readme.md Co-authored-by: Dimo Dimov <961014+dimodi@users.noreply.github.com> * Update grid/save-state-in-ProtectedBrowserStorage/readme.md Co-authored-by: Dimo Dimov <961014+dimodi@users.noreply.github.com> * Update grid/save-state-in-ProtectedBrowserStorage/readme.md Co-authored-by: Dimo Dimov <961014+dimodi@users.noreply.github.com> Co-authored-by: Dimo Dimov <961014+dimodi@users.noreply.github.com>
1 parent 3b7ba27 commit 61622dd

33 files changed

+1703
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.31515.178
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StateInProtectedBrowserStorage", "StateInProtectedBrowserStorage\StateInProtectedBrowserStorage.csproj", "{AB9E3192-FB19-4698-B9B3-CE0062861C16}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{AB9E3192-FB19-4698-B9B3-CE0062861C16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{AB9E3192-FB19-4698-B9B3-CE0062861C16}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{AB9E3192-FB19-4698-B9B3-CE0062861C16}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{AB9E3192-FB19-4698-B9B3-CE0062861C16}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {487B57C1-85A1-48B3-82A6-B67E2BD5E2EA}
24+
EndGlobalSection
25+
EndGlobal
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Router AppAssembly="typeof(Program).Assembly">
2+
<Found Context="routeData">
3+
<RouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)" />
4+
</Found>
5+
<NotFound>
6+
<h1>Page not found</h1>
7+
<p>Sorry, but there's nothing here!</p>
8+
</NotFound>
9+
</Router>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
3+
namespace StateInProtectedBrowserStorage.Data
4+
{
5+
public class WeatherForecast
6+
{
7+
public DateTime Date { get; set; }
8+
9+
public int TemperatureC { get; set; }
10+
11+
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12+
13+
public string Summary { get; set; }
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
5+
namespace StateInProtectedBrowserStorage.Data
6+
{
7+
public class WeatherForecastService
8+
{
9+
private static readonly string[] Summaries = new[]
10+
{
11+
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
12+
};
13+
14+
public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
15+
{
16+
var rng = new Random();
17+
return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast
18+
{
19+
Date = startDate.AddDays(index),
20+
TemperatureC = rng.Next(-20, 55),
21+
Summary = Summaries[rng.Next(Summaries.Length)]
22+
}).ToArray());
23+
}
24+
}
25+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
6+
namespace StateInProtectedBrowserStorage.Models
7+
{
8+
public class SampleData
9+
{
10+
public int Id { get; set; }
11+
public string Name { get; set; }
12+
public string Team { get; set; }
13+
14+
// example of comparing stored items (from editing or selection)
15+
// with items from the current data source - IDs are used instead of the default references
16+
public override bool Equals(object obj)
17+
{
18+
if (obj is SampleData)
19+
{
20+
return this.Id == (obj as SampleData).Id;
21+
}
22+
return false;
23+
}
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
@page "/explicit-serialization"
2+
3+
@using StateInProtectedBrowserStorage.Models
4+
@using StateInProtectedBrowserStorage.Services
5+
@inject GridDataService DataService
6+
@inject IJSRuntime JsInterop
7+
8+
@using System.Text.Json
9+
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
10+
@inject ProtectedLocalStorage _MsBrowserStorage
11+
12+
Change something in the grid (like sort, filter, select, page, resize columns, etc.), then reload the page to see the grid state fetched from the browser local storage.
13+
<br />
14+
15+
<TelerikButton OnClick="@ReloadPage">Reload the page to see the current grid state preserved</TelerikButton>
16+
<TelerikButton OnClick="@ResetState">Reset the state</TelerikButton>
17+
18+
<TelerikGrid Data="@GridData" Height="500px" @ref="@Grid"
19+
Groupable="true"
20+
Pageable="true"
21+
Sortable="true"
22+
FilterMode="@GridFilterMode.FilterRow"
23+
Reorderable="true"
24+
Resizable="true"
25+
SelectionMode="GridSelectionMode.Multiple" @bind-SelectedItems="@SelectedItems"
26+
OnUpdate=@UpdateItem OnDelete=@DeleteItem OnCreate=@CreateItem EditMode="@GridEditMode.Inline"
27+
OnStateInit="@((GridStateEventArgs<SampleData> args) => OnStateInitHandler(args))"
28+
OnStateChanged="@((GridStateEventArgs<SampleData> args) => OnStateChangedHandler(args))">
29+
<GridColumns>
30+
<GridColumn Field="@(nameof(SampleData.Id))" Editable="false" />
31+
<GridColumn Field="@(nameof(SampleData.Name))" Title="Employee Name" />
32+
<GridColumn Field="@(nameof(SampleData.Team))" Title="Team" />
33+
<GridCommandColumn>
34+
<GridCommandButton Command="Edit" Icon="edit">Edit</GridCommandButton>
35+
<GridCommandButton Command="Delete" Icon="delete">Delete</GridCommandButton>
36+
<GridCommandButton Command="Save" Icon="save" ShowInEdit="true">Save</GridCommandButton>
37+
<GridCommandButton Command="Cancel" Icon="cancel" ShowInEdit="true">Cancel</GridCommandButton>
38+
</GridCommandColumn>
39+
</GridColumns>
40+
<GridToolBar>
41+
<GridCommandButton Command="Add" Icon="add">Add Employee</GridCommandButton>
42+
</GridToolBar>
43+
</TelerikGrid>
44+
45+
@if (SelectedItems != null)
46+
{
47+
<ul>
48+
@foreach (SampleData employee in SelectedItems)
49+
{
50+
<li>
51+
@employee.Id
52+
</li>
53+
}
54+
</ul>
55+
}
56+
57+
@code {
58+
List<SampleData> GridData { get; set; }
59+
IEnumerable<SampleData> SelectedItems { get; set; } = Enumerable.Empty<SampleData>();
60+
61+
62+
63+
// Load and Save the state through the grid events
64+
string UniqueStorageKey = "SampleGridStateStorageKeyThatShouldBeUnique";
65+
TelerikGrid<SampleData> Grid { get; set; }
66+
67+
async Task OnStateInitHandler(GridStateEventArgs<SampleData> args)
68+
{
69+
try
70+
{
71+
ProtectedBrowserStorageResult<string> storageData = await _MsBrowserStorage.GetAsync<string>(UniqueStorageKey);
72+
if (storageData.Success && !string.IsNullOrEmpty(storageData.Value))
73+
{
74+
args.GridState = JsonSerializer.Deserialize<GridState<SampleData>>(storageData.Value);
75+
}
76+
77+
}
78+
catch (InvalidOperationException e)
79+
{
80+
// the JS Interop for the MS local storage cannot be used during pre-rendering
81+
// so the code above will throw. Once the app initializes, it will work fine
82+
// Read mor in the official documentation, as there are different ways to handle this
83+
// https://docs.microsoft.com/en-us/aspnet/core/blazor/state-management?view=aspnetcore-5.0&pivots=server
84+
}
85+
}
86+
87+
async void OnStateChangedHandler(GridStateEventArgs<SampleData> args)
88+
{
89+
await _MsBrowserStorage.SetAsync(
90+
UniqueStorageKey,
91+
JsonSerializer.Serialize(args.GridState)
92+
);
93+
}
94+
95+
async Task ResetState()
96+
{
97+
// clean up the storage
98+
await _MsBrowserStorage.DeleteAsync(UniqueStorageKey);
99+
100+
await Grid.SetState(null); // pass null to reset the state
101+
}
102+
103+
void ReloadPage()
104+
{
105+
JsInterop.InvokeVoidAsync("window.location.reload");
106+
}
107+
108+
109+
110+
111+
// Sample CRUD operations
112+
113+
async Task UpdateItem(GridCommandEventArgs args)
114+
{
115+
SampleData item = (SampleData)args.Item;
116+
117+
// perform actual data source operations here through your service
118+
await DataService.Update(item);
119+
120+
// update the local view-model data with the service data
121+
await GetGridData();
122+
123+
Console.WriteLine("Update event is fired.");
124+
}
125+
126+
async Task DeleteItem(GridCommandEventArgs args)
127+
{
128+
SampleData item = (SampleData)args.Item;
129+
130+
// perform actual data source operation here through your service
131+
await DataService.Delete(item);
132+
133+
// update the local view-model data with the service data
134+
await GetGridData();
135+
136+
Console.WriteLine("Delete event is fired.");
137+
}
138+
139+
async Task CreateItem(GridCommandEventArgs args)
140+
{
141+
SampleData item = (SampleData)args.Item;
142+
143+
// perform actual data source operation here through your service
144+
await DataService.Create(item);
145+
146+
// update the local view-model data with the service data
147+
await GetGridData();
148+
149+
Console.WriteLine("Create event is fired.");
150+
}
151+
152+
async Task GetGridData()
153+
{
154+
GridData = await DataService.Read();
155+
}
156+
157+
protected override async Task OnInitializedAsync()
158+
{
159+
await GetGridData();
160+
}
161+
}

0 commit comments

Comments
 (0)