【问题标题】:How to disable/hide a button as soon as clicked in Blazor?如何在 Blazor 中单击后立即禁用/隐藏按钮?
【发布时间】:2020-03-30 17:48:58
【问题描述】:

我们注意到,在我们的 Blazor 服务器端应用程序中,用户可能会无意中多次单击按钮。如何使用 Blazor 框架中可用的选项来防止这种情况发生?

只要他们单击按钮,将按钮的文本更改为“处理中...”就可以满足我们的要求,但我不知道如何实现这一点。点击事件的处理程序需要几秒钟来处理。

任何指针?

【问题讨论】:

  • 我尝试多次单击一个按钮,但无济于事...这太棒了...并不是说您的用户可以在闲暇时多次单击,但事实上你们已经注意到了这种神秘的现象
  • @biostat:Handler 是什么样子的,是异步的还是异步的?
  • "点击事件的处理程序需要几秒钟来处理。"所以呢 ?让他们慢慢来。您似乎没有理解处理流程:问题不在于事件处理程序执行需要多长时间,而是在单击按钮并遇到第一个等待操作符后立即经过的时间跨度,该操作符将控制权交给调用继续重新渲染组件的代码,从而禁用按钮。这需要几分之一秒。在按钮被禁用之前,任何人的手都无法触发第二次点击。
  • “异步与否”,不相关。你在努力什么?
  • 我的问题已经解决,我理解了这个概念,所以让我们关闭这个线程。正如 Henk 猜测的那样,我在实际 await 语句之前有几个同步操作,这导致将控制权交还给调用代码的延迟。我从错误中吸取了教训。

标签: blazor blazor-server-side


【解决方案1】:

您可能遇到以下两个问题之一或两者兼而有之:

  1. 延迟是一个问题。确保您在托管应用程序时启用了 Web 套接字。该设置取决于主机,例如:Azure App Servers 在设置下有一个用于 Web Sockets 的简单开/关旋钮。请参阅此博客文章中的最后一步 http://blazorhelpwebsite.com/Blog/tabid/61/EntryId/4349/Deploying-A-Server-Side-Blazor-Application-To-Azure.aspx
  2. 您有一个长时间运行的任务。

对于长时间运行的任务,您可以尝试以下解决方案。我没有测试过,里程可能会有所不同。

<button disabled=@IsTaskRunning @onclick="DispatchTask">Submit</button>

@code {

    bool IsTaskRunning = false;

    async void DispatchTask()
    {
        IsTaskRunning = true;

        await DoLongWork();

        IsTaskRunning = false;
        StateHasChanged();
    }

    Task DoLongWork()
    {
        return Task.Delay(6000);
    }

}

【讨论】:

  • 第二个选项没有帮助(仍然可以单击多次)。我正在研究第一个选项。
  • 用博客资源更新了我的答案。
  • async void 是危险且不必要的。 Blazor 接受 async Task habdlers。
  • 请解释一下为什么它是危险和不必要的?
  • async void 会导致未处理的异常“丢失”。然而,这只是一个粗略的例子,并不意味着按原样使用。我也不建议任何人将方法命名为 DoLongWork。此外,Async void 在 Blazor 中实际上是可以接受的(不是我的偏好)。请参阅 Blazor.net 上的文档,Microsoft 也使用它。
【解决方案2】:

这最适合异步处理程序方法,因为

async Task DispatchTask()
{
    IsTaskRunning = true;

    await DoLongWork();

    IsTaskRunning = false;
    // StateHasChanged();  // called automatically
}

这是来自@EdCharbeneau 的回答,但请注意它现在是async task 方法。

但这仅在 DoLongWork() 真正异步时才有效。这意味着它必须在早期进行一些实际的异步 I/O。您不能总是说,可等待的方法可能(有时)完全或大部分同步执行。当这种情况发生时,您的 GUI 不会更新。

假设 DoWork() 方法看起来更像

Task DoLongWork()
{
    Thread.Sleep(6000);   // synchronous
    return Task.CompletedTask;
}

那么这仍然会禁用按钮:

async Task DispatchTask()
{
    IsTaskRunning = true;
    await Task.Delay(1);  // allow the GUI to catch up

    await DoLongWork();   // fake async

    IsTaskRunning = false;        
}

【讨论】:

  • await Task.Delay(1) 带来了天壤之别。我无法多次单击,那么,这是否意味着可等待的方法正在执行某种同步操作?你能解释一下在幕后引入延迟有什么作用吗?
  • 它执行一个隐含的 StateHasChanged() 并允许渲染引擎运行。
  • “让 GUI 迎头赶上”,这到底是什么意思?注意解释调用 await Task.Delay(1) 有何不同。无论您是否使用它,在遇到第一个 await 操作符后立即将控制权交给继续重新渲染组件的调用代码。等待 DoLongWork();足够的。如果您调用 Task.Delay(100000000) 会怎样。这会使重新渲染时间更长吗?)
  • 它“改变了整个世界”。
  • @biostat 这里也一样。在工作发生时,我必须使用Task.Delay(1) 显示“微调器”按钮。感觉就像一个黑客,但我看不到另一种方式。 Task.Delay(1) 在 Windows 上实际上是 15 毫秒,因为方法是 async,所以 UI 是空闲的,并且有时间在 15 毫秒内更新微调器按钮。我需要await Taks.Delay(1),因为我至少需要一个await 来创建方法async。我调用的所有内容都是同步的。
【解决方案3】:

通用解决方案怎么样?微调按钮

可以很容易地编辑它以使用标准按钮。 Generate a SVG animation here.

(Radzen 和 Loading.io 都是免费的)

<RadzenButton Text="@(IsProcessing ? null : Text)" Image="@(IsProcessing ? Spinner : null)" Disabled="@IsProcessing" @attributes="@AdditionalAttributes" />

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

    [Parameter]
    public string Spinner { get; set; } = "/images/spinner-button.svg";

    [Parameter(CaptureUnmatchedValues = true)]
    public IDictionary<string, object>? AdditionalAttributes { get; set; }

    public bool IsProcessing { get; private set; }

    [Parameter]
    public EventCallback OnSubmit { get; set; }

    public async Task FormSubmitAsync()
    {
        if (IsProcessing) { return; }

        IsProcessing = true;
        try
        {
            await OnSubmit.InvokeAsync(null);
        }
        finally
        {
            IsProcessing = false;
        }
    }
}

这样使用

<EditForm OnValidSubmit="@SubmitAsync">
    <SpinnerButton @ref="ButtonRef" style="width:150px" Text="Login" 
        ButtonType="@((Radzen.ButtonType)ButtonType.Submit)" 
        ButtonStyle="@((Radzen.ButtonStyle)ButtonStyle.Primary)"
        OnSubmit="LogInAsync" />
</EditForm>

@code {
    public SpinnerButton? ButtonRef { get; set; }
    public async Task SubmitAsync() => await ButtonRef!.FormSubmitAsync().ConfigureAwait(false);

    public async Task LogInAsync()
    {
        // Get stuff done!
    }
}

【讨论】:

    【解决方案4】:
    private Dictionary<string, string> control = new Dictionary<string, string>();
    
    function click(){
        lock (control)
            {
                if (!control.ContainsKey("click"))
                {
                control.Add("click", "executing");
                }
                else
                {
                if (control["click"] == "executing")
                {
                    //Double click detected
                    return;
                }
                else
                {
                    control["click"] = "executing";
                }
                }
            }
    
    
    //Do usual stuffs here
    
    
        lock (control)
        {
            control["click"] = "";
        }
    

    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-13
      • 1970-01-01
      • 2021-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多