Reputation: 1811
I have an EditForm in Blazor (server side) with an InputText control. The form is bound to a model class and the InputText field is bound to a single property that is backed by local storage.
When the page loads, the InputText shows a blank value. I call a method InitAsync to load data from the local storage asynchronously. This works, but setting the property with the loaded data does not update the view.
The same problem occurs if I bind the value to a span tag with one-way binding instead of an InputText (which has two-way binding).
How do I get the View to update after loading data asynchronously from storage?
View/component class:
@inject Blazored.LocalStorage.ILocalStorageService localStorage
<EditForm Model="@model" class="pl-4 pr-3 py-1 loginform" OnValidSubmit="@HandleValidSubmit">
<span>model.OperID</span>
<InputText class="form-control mr-sm-2" style="width: 158px" placeholder="OperID" @bind-Value="@model.OperID"></InputText>
<button class="btn btn-success oi oi-account-login" @onclick="Login"></button>
</EditForm>
@code {
private Models.MainModel model = new Models.MainModel();
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await model.InitializeAsync(localStorage);
}
void Login()
{
}
private void HandleValidSubmit()
{
Console.WriteLine("OnValidSubmit");
}
}
Model class:
public class MainModel
{
Blazored.LocalStorage.ILocalStorageService? localStorage;
public MainModel()
{
}
public async Task InitializeAsync(Blazored.LocalStorage.ILocalStorageService localStorage)
{
this.localStorage = localStorage;
if (localStorage != null)
{
// the value loads correctly, but setting the property does not cause the view to update as it should.
// ConfigureAwait(false) makes no difference...
OperID = await localStorage.GetItemAsync<string?>("OperID").ConfigureAwait(false);
}
}
string? operID = "DUMMY"; // null;
public string? OperID
{
get
{
return operID;
}
set
{
operID = value;
if (localStorage != null)
{
localStorage.SetItemAsync("OperID", value);
}
}
}
}
Upvotes: 4
Views: 4661
Reputation: 45626
Since you're using server-side Blazor and probably the setting for pre-rendering is active, you cannot and should not use the OnIntialized or OnInitializedAsync methods. Instead you should use the OnAfterRenderAsync method, which is the appropriate place to use JSInterop. And you're code should be enclosed within an if statement checking if this is the first rendering, like this:
protected override async Task OnAfterRenderAsync(bool firstRender)
{ if( firstRender)
{
await model.InitializeAsync(localStorage);
// And of course the StateHasChanged method should be called here to refresh the page.
StateHasChanged();
}
}
That way your model is initialized only once, and not every time the OnAfterRenderAsync is executed, which is whenever the page components need rerendering.
Note: If you are re-rendering, the OnAfterRenderAsync method is not called at all.
The following code snippet from the _Host.cshtml can tell if you're using pre-rendering or not:
<app>
<component type="typeof(App)" render-mode="ServerPrerendered" />
</app>
As you can see from the setting your app is pre-rendering on the server. Does Blazored.LocalStorage supports Data Protection, if not. Use the library created by the Blazor team.
Upvotes: 3
Reputation: 1811
This is the code I changed to get it to the work:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender) // prevent infinite loop
{
await model.InitializeAsync(localStorage); // load data
StateHasChanged(); // update view
}
}
Upvotes: 5