- load on show page and save via buttonmaster
| <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> | |||||
| <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> |
| <div class="row no-gutters align-items-start w-100"> | <div class="row no-gutters align-items-start w-100"> | ||||
| <div class="row no-gutters w-100" style="padding-top:2em"> | <div class="row no-gutters w-100" style="padding-top:2em"> | ||||
| <div class="col-6" style="padding-right:0.5em"> | <div class="col-6" style="padding-right:0.5em"> | ||||
| <MatStringField Class="w-100" Label="Firstname" Outlined="true" type="text" @bind-Value="@State.Firstname"></MatStringField> | |||||
| <MatStringField Class="w-100" Label="Firstname" Outlined="true" type="text" @bind-Value="@Account.Firstname"></MatStringField> | |||||
| </div> | </div> | ||||
| <div class="col-6" style="padding-left:0.5em"> | <div class="col-6" style="padding-left:0.5em"> | ||||
| <MatStringField Class="w-100" Label="Lastname" Outlined="true" type="text" @bind-Value="@State.Lastname"></MatStringField> | |||||
| <MatStringField Class="w-100" Label="Lastname" Outlined="true" type="text" @bind-Value="@Account.Lastname"></MatStringField> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="row no-gutters align-items-center w-100"> | <div class="row no-gutters align-items-center w-100"> | ||||
| <div class="col-12"> | <div class="col-12"> | ||||
| <MatStringField Class="w-100" Label="Address" Outlined="true" type="text" @bind-Value="@State.Address"></MatStringField> | |||||
| <MatStringField Class="w-100" Label="Address" Outlined="true" type="text" @bind-Value="@Account.Address"></MatStringField> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="row no-gutters align-items-center w-100"> | <div class="row no-gutters align-items-center w-100"> | ||||
| <div class="col-4" style="padding-right:0.5em"> | <div class="col-4" style="padding-right:0.5em"> | ||||
| <MatStringField Class="w-100" Label="Zip" Outlined="true" type="text" @bind-Value="@State.Zip"></MatStringField> | |||||
| <MatStringField Class="w-100" Label="Zip" Outlined="true" type="text" @bind-Value="@Account.Zip"></MatStringField> | |||||
| </div> | </div> | ||||
| <div class="col-8" style="padding-left:0.5em"> | <div class="col-8" style="padding-left:0.5em"> | ||||
| <MatStringField Class="w-100" Label="City" Outlined="true" type="text" @bind-Value="@State.City"></MatStringField> | |||||
| <MatStringField Class="w-100" Label="City" Outlined="true" type="text" @bind-Value="@Account.City"></MatStringField> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="row no-gutters align-items-center w-100"> | <div class="row no-gutters align-items-center w-100"> | ||||
| <div class="col-12"> | <div class="col-12"> | ||||
| <MatStringField Class="w-100" Label="Phone" Outlined="true" type="text" @bind-Value="@State.Phone"></MatStringField> | |||||
| <MatStringField Class="w-100" Label="Phone" Outlined="true" type="text" @bind-Value="@Account.Phone"></MatStringField> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="row no-gutters align-items-center w-100"> | <div class="row no-gutters align-items-center w-100"> | ||||
| <div class="col-12"> | <div class="col-12"> | ||||
| <MatStringField Class="w-100" Label="E-Mail" Outlined="true" type="text" @bind-Value="@State.Email"></MatStringField> | |||||
| <MatStringField Class="w-100" Label="E-Mail" Outlined="true" type="text" @bind-Value="@Account.Email"></MatStringField> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @code { | @code { | ||||
| async private void SaveUserData() { | |||||
| protected async override void OnInitialized() { | |||||
| await GetUserData(); | |||||
| } | |||||
| private async void SaveUserData() { | |||||
| await UserDataProvider.Save(); | await UserDataProvider.Save(); | ||||
| } | } | ||||
| [CascadingParameter] | |||||
| public UserData State { get; set; } | |||||
| } | |||||
| private async Task GetUserData() { | |||||
| await InvokeAsync(async () => { | |||||
| await UserDataProvider.Get(); | |||||
| StateHasChanged(); | |||||
| }); | |||||
| } | |||||
| private UserData Account { | |||||
| get => UserDataProvider.Data; | |||||
| } | |||||
| } | |||||
| namespace CaritasPWA.Shared { | namespace CaritasPWA.Shared { | ||||
| // The class that stores the user settings | // The class that stores the user settings | ||||
| public class UserData : INotifyPropertyChanged { | |||||
| public class UserData { | |||||
| private string firstname; | private string firstname; | ||||
| private string lastname; | private string lastname; | ||||
| private string address; | private string address; | ||||
| private string email; | private string email; | ||||
| public string Firstname { get => firstname; set { firstname = value; RaisePropertyChanged(); } } | |||||
| public string Lastname { get => lastname; set { lastname = value; RaisePropertyChanged(); } } | |||||
| public string Email { get => email; set { email = value; RaisePropertyChanged(); } } | |||||
| public string Address { get => address; set { address = value; RaisePropertyChanged(); }} | |||||
| public string Zip { get => zip; set { zip = value; RaisePropertyChanged(); } } | |||||
| public string City { get => city; set { city = value; RaisePropertyChanged(); } } | |||||
| public string Phone { get => phone; set { phone = value; RaisePropertyChanged(); } } | |||||
| public event PropertyChangedEventHandler PropertyChanged; | |||||
| private void RaisePropertyChanged([CallerMemberName] string propertyName = null) { | |||||
| PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); | |||||
| } | |||||
| public string Firstname { get => firstname; set { firstname = value; } } | |||||
| public string Lastname { get => lastname; set { lastname = value; } } | |||||
| public string Address { get => address; set { address = value; } } | |||||
| public string Zip { get => zip; set { zip = value; } } | |||||
| public string City { get => city; set { city = value; } } | |||||
| public string Phone { get => phone; set { phone = value; } } | |||||
| public string Email { get => email; set { email = value; } } | |||||
| } | } | ||||
| } | } |
| @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(); | |||||
| } | |||||
| } |
| namespace CaritasPWA.Shared { | namespace CaritasPWA.Shared { | ||||
| public sealed class UserDataProvider { | public sealed class UserDataProvider { | ||||
| private const string KeyName = "state"; | |||||
| private const string KeyName = "account"; | |||||
| private readonly IJSRuntime _jsRuntime; | private readonly IJSRuntime _jsRuntime; | ||||
| private bool _initialized; | private bool _initialized; | ||||
| private UserData _data; | |||||
| private UserData _data = new UserData(); | |||||
| public UserData Data { | |||||
| get => _data; | |||||
| set => _data = value; | |||||
| } | |||||
| public event EventHandler Changed; | public event EventHandler Changed; | ||||
| public bool AutoSave { get; set; } = true; | public bool AutoSave { get; set; } = true; | ||||
| } | } | ||||
| public async ValueTask<UserData> Get() { | public async ValueTask<UserData> Get() { | ||||
| if (_data != null) | |||||
| return _data; | |||||
| // Register the Storage event handler. This handler calls OnStorageUpdated when the storage changed. | // 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 | // This way, you can reload the settings when another instance of the application (tab / window) save the settings | ||||
| result = new UserData(); | 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; | _data = result; | ||||
| return result; | return result; | ||||
| } | } | ||||
| await _jsRuntime.InvokeVoidAsync("BlazorSetLocalStorage", KeyName, json); | 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 | // This method is called from BlazorRegisterStorageEvent when the storage changed | ||||
| [JSInvokable] | [JSInvokable] | ||||
| public void OnStorageUpdated(string key) { | public void OnStorageUpdated(string key) { |