biostat
biostat

Reputation: 565

How to disable/hide a button as soon as clicked in Blazor?

We are noticing that in our Blazor server-side application, users are able to click a button more than once unintentionally. How can I prevent this using options available in Blazor framework?

Anything as simple as changing the text of the button to "processing..." as soon as they click the button will suffice our requirements but I am not sure how to achieve this. Handler for click event takes few seconds to process.

Any pointers?

Upvotes: 17

Views: 17045

Answers (4)

Henk Holterman
Henk Holterman

Reputation: 273179

<button class="btn" disabled="@isTaskRunning" @onclick="DispatchTask">Click me</button>

This works best with an async Task handler method, as

async Task DispatchTask()   // avoid async void
{
    isTaskRunning = true;
    try 
    {
      StateHasChanged();      // [1] we want isTaskRunning to take effect
      await Task.Delay(1);    // don't rely on DoLongWork() executing async

      await DoLongWork();
    }
    finally
    {
      isTaskRunning = false;
      // StateHasChanged();  // only needed in an async void
    }
}

Suppose the DoWork() method looks like

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

Then it executes synchronously, despite the async. The Task.Delay(1) remedies that.

[1] : there is already a StateHasChanged pending but it's clearer to call it here and it's very cheap.

Upvotes: 17

Etienne Charland
Etienne Charland

Reputation: 4024

How about a generic solution? SpinnerButton

It could easily be edited to use a standard button. Generate a SVG animation here.

(both Radzen and Loading.io are free)

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

Use like this

<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!
    }
}

Upvotes: 2

Nick Chan Abdullah
Nick Chan Abdullah

Reputation: 375

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"] = "";
    }

}

Upvotes: -2

Ed Charbeneau
Ed Charbeneau

Reputation: 4624

You likely have one of two problems or both:

  1. Latency is an issue. Make sure you are hosting your application with Web Sockets enabled. The setting is dependent on the host, ex: Azure App Servers has a simple on/off knob for Web Sockets under settings. See the very last step in this blog post http://blazorhelpwebsite.com/Blog/tabid/61/EntryId/4349/Deploying-A-Server-Side-Blazor-Application-To-Azure.aspx
  2. You have a long running task.

For a long running task you can try the following solution. I have not tested it, mileage may vary.

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

}

Upvotes: 8

Related Questions