| @@ -1,10 +1,12 @@ | |||
| <Router AppAssembly="@typeof(Program).Assembly"> | |||
| <Found Context="routeData"> | |||
| <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> | |||
| </Found> | |||
| <NotFound> | |||
| <LayoutView Layout="@typeof(MainLayout)"> | |||
| <p>Sorry, there's nothing at this address.</p> | |||
| </LayoutView> | |||
| </NotFound> | |||
| </Router> | |||
| <UserDataComponent> | |||
| <Router AppAssembly="@typeof(Program).Assembly"> | |||
| <Found Context="routeData"> | |||
| <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> | |||
| </Found> | |||
| <NotFound> | |||
| <LayoutView Layout="@typeof(MainLayout)"> | |||
| <p>Sorry, there's nothing at this address.</p> | |||
| </LayoutView> | |||
| </NotFound> | |||
| </Router> | |||
| </UserDataComponent> | |||
| @@ -1,22 +1,56 @@ | |||
| @page "/account" | |||
| @inject NavigationManager NavigationManager | |||
| @inject UserDataProvider UserDataProvider | |||
| <h1>Counter</h1> | |||
| <div class="row px-3 h-100"> | |||
| <div class="row no-gutters align-items-start w-100"> | |||
| <div class="row no-gutters w-100" style="padding-top:2em"> | |||
| <div class="col-6" style="padding-right:0.5em"> | |||
| <MatStringField Class="w-100" Label="Firstname" Outlined="true" type="text" @bind-Value="@State.Firstname"></MatStringField> | |||
| </div> | |||
| <div class="col-6" style="padding-left:0.5em"> | |||
| <MatStringField Class="w-100" Label="Lastname" Outlined="true" type="text" @bind-Value="@State.Lastname"></MatStringField> | |||
| </div> | |||
| </div> | |||
| <div class="row no-gutters align-items-center w-100"> | |||
| <div class="col-12"> | |||
| <MatStringField Class="w-100" Label="Address" Outlined="true"></MatStringField> | |||
| </div> | |||
| </div> | |||
| <div class="row no-gutters align-items-center w-100"> | |||
| <div class="col-4" style="padding-right:0.5em"> | |||
| <MatStringField Class="w-100" Label="Zip" Outlined="true"></MatStringField> | |||
| </div> | |||
| <div class="col-8" style="padding-left:0.5em"> | |||
| <MatStringField Class="w-100" Label="City" Outlined="true"></MatStringField> | |||
| </div> | |||
| </div> | |||
| <div class="row no-gutters align-items-center w-100"> | |||
| <div class="col-12"> | |||
| <MatStringField Class="w-100" Label="Phone" Outlined="true"></MatStringField> | |||
| </div> | |||
| </div> | |||
| <div class="row no-gutters align-items-center w-100"> | |||
| <div class="col-12"> | |||
| <MatStringField Class="w-100" Label="E-Mail" Outlined="true"></MatStringField> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="row no-gutters align-items-end justify-content-center w-100" style="padding-bottom:2em"> | |||
| <MatButton Class="w-100" Raised="true" @onclick="SaveUserData">Speichern</MatButton> | |||
| </div> | |||
| </div> | |||
| <p>Current count: @currentCount</p> | |||
| <MatList> | |||
| <MatListItem> | |||
| <MatButton Raised="true" @onclick="IncrementCount">Click me!</MatButton> | |||
| </MatListItem> | |||
| </MatList> | |||
| @code { | |||
| private int currentCount = 0; | |||
| private void IncrementCount() { | |||
| currentCount++; | |||
| async private void SaveUserData() { | |||
| await UserDataProvider.Save(); | |||
| } | |||
| [CascadingParameter] | |||
| public UserData State { get; set; } | |||
| } | |||
| @@ -19,6 +19,7 @@ namespace CaritasPWA { | |||
| builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); | |||
| builder.Services.AddMatBlazor(); | |||
| builder.Services.AddSingleton<AppState>(); | |||
| builder.Services.AddScoped<UserDataProvider>(); | |||
| await builder.Build().RunAsync(); | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| using System.ComponentModel; | |||
| using System.Runtime.CompilerServices; | |||
| namespace CaritasPWA.Shared { | |||
| // The class that stores the user settings | |||
| public class UserData : INotifyPropertyChanged { | |||
| private string firstname; | |||
| private string lastname; | |||
| public string Firstname { | |||
| get => firstname; | |||
| set { | |||
| firstname = value; | |||
| RaisePropertyChanged(); | |||
| } | |||
| } | |||
| public string Lastname { | |||
| get => lastname; | |||
| set { | |||
| lastname = value; | |||
| RaisePropertyChanged(); | |||
| } | |||
| } | |||
| public event PropertyChangedEventHandler PropertyChanged; | |||
| private void RaisePropertyChanged([CallerMemberName] string propertyName = null) { | |||
| PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,35 @@ | |||
| @inject UserDataProvider UserDataProvider | |||
| @implements IDisposable | |||
| @if (state == null) { | |||
| <p>loading...</p> | |||
| } else { | |||
| <CascadingValue Value="@state" IsFixed="false">@ChildContent</CascadingValue> | |||
| } | |||
| @code{ | |||
| private UserData state = null; | |||
| [Parameter] | |||
| public RenderFragment ChildContent { get; set; } | |||
| protected override async Task OnInitializedAsync() { | |||
| UserDataProvider.Changed += UserDataChanged; | |||
| await Refresh(); | |||
| } | |||
| public void Dispose() { | |||
| UserDataProvider.Changed -= UserDataChanged; | |||
| } | |||
| private async void UserDataChanged(object sender, EventArgs e) { | |||
| await InvokeAsync(async () => { | |||
| await Refresh(); | |||
| StateHasChanged(); | |||
| }); | |||
| } | |||
| private async Task Refresh() { | |||
| state = await UserDataProvider.Get(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,72 @@ | |||
| using Microsoft.JSInterop; | |||
| using System; | |||
| using System.ComponentModel; | |||
| using System.Threading.Tasks; | |||
| namespace CaritasPWA.Shared { | |||
| public sealed class UserDataProvider { | |||
| private const string KeyName = "state"; | |||
| private readonly IJSRuntime _jsRuntime; | |||
| private bool _initialized; | |||
| private UserData _data; | |||
| public event EventHandler Changed; | |||
| public bool AutoSave { get; set; } = true; | |||
| public UserDataProvider(IJSRuntime jsRuntime) { | |||
| _jsRuntime = jsRuntime; | |||
| } | |||
| public async ValueTask<UserData> Get() { | |||
| if (_data != null) | |||
| return _data; | |||
| // Register the Storage event handler. This handler calls OnStorageUpdated when the storage changed. | |||
| // This way, you can reload the settings when another instance of the application (tab / window) save the settings | |||
| if (!_initialized) { | |||
| // Create a reference to the current object, so the JS function can call the public method "OnStorageUpdated" | |||
| var reference = DotNetObjectReference.Create(this); | |||
| await _jsRuntime.InvokeVoidAsync("BlazorRegisterStorageEvent", reference); | |||
| _initialized = true; | |||
| } | |||
| // Read the JSON string that contains the data from the local storage | |||
| UserData result; | |||
| var str = await _jsRuntime.InvokeAsync<string>("BlazorGetLocalStorage", KeyName); | |||
| if (str != null) { | |||
| result = System.Text.Json.JsonSerializer.Deserialize<UserData>(str) ?? new UserData(); | |||
| } else { | |||
| result = new UserData(); | |||
| } | |||
| // Register the OnPropertyChanged event, so it automatically persists the settings as soon as a value is changed | |||
| result.PropertyChanged += OnPropertyChanged; | |||
| _data = result; | |||
| return result; | |||
| } | |||
| public async Task Save() { | |||
| var json = System.Text.Json.JsonSerializer.Serialize(_data); | |||
| await _jsRuntime.InvokeVoidAsync("BlazorSetLocalStorage", KeyName, json); | |||
| } | |||
| // Automatically persist the settings when a property changed | |||
| private async void OnPropertyChanged(object sender, PropertyChangedEventArgs e) { | |||
| if (AutoSave) { | |||
| await Save(); | |||
| } | |||
| } | |||
| // This method is called from BlazorRegisterStorageEvent when the storage changed | |||
| [JSInvokable] | |||
| public void OnStorageUpdated(string key) { | |||
| if (key == KeyName) { | |||
| // Reset the settings. The next call to Get will reload the data | |||
| _data = null; | |||
| Changed?.Invoke(this, EventArgs.Empty); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -17,6 +17,7 @@ | |||
| </head> | |||
| <body> | |||
| <app>Loading...</app> | |||
| <div id="blazor-error-ui"> | |||
| @@ -26,6 +27,25 @@ | |||
| </div> | |||
| <script src="_framework/blazor.webassembly.js"></script> | |||
| <script>navigator.serviceWorker.register('service-worker.js');</script> | |||
| <script> | |||
| function BlazorSetLocalStorage(key, value) { | |||
| localStorage.setItem(key, value); | |||
| } | |||
| function BlazorGetLocalStorage(key) { | |||
| return localStorage.getItem(key); | |||
| } | |||
| function BlazorRegisterStorageEvent(component) { | |||
| window.addEventListener("storage", async e => { | |||
| await component.invokeMethodAsync("OnStorageUpdated", e.key); | |||
| }); | |||
| } | |||
| </script> | |||
| </body> | |||
| </html> | |||