Blazor (Client-side) StateHasChanged() not updating page

bob82 picture bob82 · Jan 3, 2020 · Viewed 8.2k times · Source

I have a button on my page (component) that calls a Refresh() method when clicked. This method then calls the StateHasChanged(), but does not reload the page. The GetData() is calling an external API to load data from a database.

<button class="btn btn-warning" @onclick="Refresh">Refresh</button>

code {

protected override async Task OnInitializedAsync()
    {
        try
        {
            await GetData();
            base.OnInitialized();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }

    }

protected async Task GetData()
    {
        try
        {
            results = await HttpClient.GetJsonAsync<Results[]>(ServiceEndpoints.GET_RESULTS);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error retrieving data from Oracle.", ex.Message);
        }
    }

public void Refresh()
        {
            StateHasChanged();          
        }

My page (component) also has a form that has preloaded inputs that can be changed. I want the user to be able to edit the form, but be able to refresh the page by clicking the button to get back the original data. This was working on a previous version of Blazor, is there a known issue about this?

@page "/"
@inherits IndexBase

<EditForm Model="@results">
<label><strong>Select a job to view its current parameters</strong></label>
<div class="currentJobForm">

    <InputSelect id="jobSelect" @bind-Value="jobSelected">
        @foreach (var items in results)
        {
            <option value="@items.JOB_NAME">@items.JOB_NAME</option>
        }
    </InputSelect>
    <button class="btn btn-warning" @onclick="SaveChanges" disabled="@IsDisabled">Save</button>
    <button class="btn btn-warning" @onclick="Refresh">Refresh</button>
    <button class="btn btn-danger" @onclick="DeleteJob">Delete Job</button>
</div>    
    <label><strong>Notify Parameters</strong></label>
    <div class="notifyParametersForm">
        @foreach (var item in results.Where(i => i.JOB_NAME == jobSelected))
        {
            <div class="issueDescription">
                <label><strong>Issue Description</strong></label>
                <InputText id="issueDesc" @bind-Value="item.JOB_HEADER" placeholder="Enter New Issue Description" />
            </div>
            <div class="sendSlack">
                <label><strong>Send Slack</strong></label>
                <InputSelect id="sendSlackSelect" @bind-Value="item.SENDSLACK">
                    @foreach (var items in InitializeData.SendSlacks)
                    {
                        <option value="@items.SendSlackName">@items.SendSlackName</option>
                    }                        
                </InputSelect>
            </div>
            <div class="slackUser">
                <label><strong>Slack User</strong></label>
                <InputText id="slackUser" @bind-Value="item.SLACK_USER" placeholder="Enter New Slack User" />
            </div>
            <div class="slackChannel">
                <label><strong>Slack Channel</strong></label>
                <InputSelect id="sendSlackChannel" @bind-Value="item.SLACK_CHANNEL">
                    @foreach (var items in InitializeData.SlackChannels)
                    {
                        <option value="@items.SlackChannelName">@items.SlackChannelName</option>
                    }                        
                </InputSelect>
            </div>
            <div class="slackUrl">
                <label><strong>Slack URL</strong></label>
                <InputText id="slackUrlTextBox" @bind-Value="item.SLACK_URL" placeholder="Enter New Slack Url" />
            </div>
            <div class="sendMail">
                <label><strong>Send Mail</strong></label>
                <InputSelect id="sendMailSelect" @bind-Value="item.SENDMAIL">
                    @foreach (var items in InitializeData.SendMails)
                    {
                        <option value="@items.SendMailName">@items.SendMailName</option>
                    }
                </InputSelect>
            </div>
            <div class="mailFrom">
                <label><strong>From:</strong></label>
                <InputText id="from" @bind-Value="item.MAILFROM" placeholder="Enter New Mail From" />
            </div>
            <div class="mailTo">
                <label><strong>To:</strong></label>
                <InputText id="to" @bind-Value="item.MAILTO" placeholder="Enter New Mail To" />
            </div>
            <div class="subject">
                <label id="subjectLabel"><strong>Subject:</strong></label>
                <InputText id="subject" @bind-Value="item.EMAIL_SUBJECT" placeholder="Enter New Subject" />
            </div>
        }
    </div>
</div>

Answer

bortronx picture bortronx · Apr 20, 2020

As All the other answers explain there are various way to properly reset your form but none of them except BrinkDaDrink answer tell you how to refresh the page with a press of a button.

BrinkDaDrink answer add unnecessary complexity in my opinion.

My solution is to create an empty component page called refresh as follows:

@page "/refresh"
@inject NavigationManager NavigationManager

@code {

    protected override void OnInitialized()
    {
        NavigationManager.NavigateTo("");
    }

}

This page will immediately redirect to your original page refreshing the page and all of the data instantly.

You can create a refresh method in your original page as follows:

public void RefreshPage()
{
    NavigationManager.NavigateTo("refresh");
}

This solves the issue you mention much faster than any of the other solutions provided so far:

I want the user to be able to edit the form, but be able to refresh the page by clicking the button to get back the original data

You can also use this to pass parameters into your refresh page by adding the following to the refresh page:

@page "/refresh"
@page "/refresh/{text}"
@inject NavigationManager NavigationManager

@code {
    [Parameter]
    public string Text { get; set; }

    protected override void OnInitialized()
    {
        NavigationManager.NavigateTo(Text);
    }

}