Blazor(客户端)的StateHasChanged()未更新页面

9

我在页面(组件)上有一个按钮,点击后会调用Refresh()方法。然后该方法会调用StateHasChanged(),但不会重新加载页面。 GetData() 调用外部 API 从数据库中加载数据。

<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();          
        }

我的页面(组件)还有一个表单,其中包含可以更改的预加载输入。我希望用户能够编辑表单,但点击按钮以获取原始数据来刷新页面。这在之前的 Blazor 版本上是可行的,是否已知此问题?

@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>

1
StateHasChanged 不会刷新页面,它只会根据 Render Tree 更新任何需要更新的 dom 元素。如果没有看到所有代码,很难进一步评论,但某种程度上,Blazor 可能认为没有任何需要更新的元素。我猜测输入已经绑定回 Blazor,而 Blazor 将当前 dom 视为与渲染树相同。您需要先将输入设置回其原始值。 - Kyle
我刚刚包含了我的索引组件的一个部分。 - bob82
1
如果您想让输入恢复到其原始值,则需要将项目对象设置回其原始值,然后它应该更新。 - Kyle
1
你能否将你的代码重写为一个最小可复现示例呢?这样做可以帮助其他人更好地理解你的问题,快速得到答案,并且也可以帮助其他有类似问题的用户。请不要浪费别人的时间,只需提供能够理解和复现问题的代码即可。抄袭自己的代码并不礼貌,应该为问题编写一个自定义示例。 - dani herrera
3个回答

8

正如其他答案所解释的那样,有多种正确重置表单的方法,但除了BrinkDaDrink的答案外,没有一种方法能让你通过按一个按钮来刷新页面。

在我看来,BrinkDaDrink的回答增加了不必要的复杂性。

我的解决方案是创建一个名为refresh的空组件页面,如下所示:

@page "/refresh"
@inject NavigationManager NavigationManager

@code {

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

}

这个页面会立即重定向到您的原始页面,刷新页面并立即刷新所有数据。

您可以在原始页面中创建一个刷新方法,如下所示:

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

这个解决方案比到目前为止提供的其他任何解决方案都要快:

我希望用户能够编辑表单,但是点击按钮刷新页面以获取原始数据

您还可以通过在刷新页面添加以下内容来将参数传递到刷新页面:

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

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

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

}

0

我通过绑定字符串值来实现。您不必在页面正文中显示该值即可使其工作。我将字符串从大写更改为小写。有趣的是,这仅适用于字符串值。

NAVMENU ----------------------
  <button type="button" class="btn btn-warning border-secondary" @onclick="runSearch">Search</button>

@code {
    public string pageReload = "Search";

 private void runSearch()
    {
        if (pageReload == "Search") { pageReload = "search"; } else { pageReload = "Search"; }
        NavigationManager.NavigateTo($"/Page2/{pageReload}", false);
    }
}

Page 2------------------------------------------------------------------------

@page "/Datatest2/{pageReload}"

<h1>page will now refresh every time</h1>


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

}

0

您需要将值设置回其原始值。StateHasChanged不会刷新页面。它会重置组件以检查RenderTree,以查看dom元素是否应更新 - 但由于您的值没有更改,因此它不会按照您所期望的方式进行更新。

在您的情况下,调用GetData()而不是StateHasChanged应该可以解决问题,但您可能希望存储这些值而不是再次访问API。

我简化了您的代码以提供一个可工作的示例:

    @page  "/update-test"
<EditForm Model="@Item">
    <div class="notifyParametersForm">
    </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>

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

@code {


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

    }

    protected void GetData()
    {
        Item = new EmailItem()
        {
            MAILFROM = "testfrom@test.com",
            MAILTO = "testto@test.com",
            EMAIL_SUBJECT = "subject",
        };
    }

    public void ResetInputs()
    {
        GetData();
    }

    public class EmailItem
    {
        public string MAILTO { get; set; }
        public string MAILFROM { get; set; }
        public string EMAIL_SUBJECT { get; set; }
    }
}

我实际上已经尝试过这样做。它可以工作,但并不总是(有时我必须点击刷新按钮几次)。可能是因为我正在调用数据的API? - bob82
另外,当调用API时,我将值存储在一个类中。 - bob82
我唯一的猜测就是你的API有时候会挂起。如果你有一个可重现的例子,我很乐意看一下。 - Kyle
就存储值而言,请确保您的原始值存储在哪里以及您用作模型的对象不是引用到同一位置。您可以设置断点并检查您存储原始值的位置是否也在更改表单上的输入值时发生了变化。 - Kyle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接