5

I use a Boolean property to disable a button after user clicking it, so that the click does not happen twice.

 private bool DisablePlaceOrderButton { get; set; } = false;
 private void PlaceOrder_Clicked()
    {
        DisablePlaceOrderButton = true;
        StateHasChanged();
        if (cart.Lines.Count() == 0)
        {
            DisablePlaceOrderButton = false;
            return;
        }
        ...
}

The button is designed as follows:

<button @onclick="@PlaceOrder_Clicked" disabled="@DisablePlaceOrderButton">Place Order</button>

But this is not working and button is disabled only after PlaceOrder method finishes executing. I have the same problem with an ajax indicator that after method execution, should be showed and after finishing should hide, Using an if block. In both cases statehaschanged has no effect. I use Blazor Server side preview 9. How to make these work?

mz1378
  • 1,957
  • 4
  • 20
  • 40
  • 5
    StateHasChanged informs the component that its state has changed but it does not render the component. The component will decide when to render itself. You can't do that in a synchronous method, you should async your code to let the component a chance to render. – agua from mars Jan 05 '20 at 10:31

1 Answers1

5

Please, read the comment made by agua from mars, and follow this code sample to see how it works... When you click on the button, the DisablePlaceOrder method is called, the title text changes to reflect the on-going action, during which the button is disabled. This occurs in the span of 3 seconds, including the re-rendering of the control to reflect the visual changes. Note that no call to StateHasChanged is made.

In real-life application, of course, you don't use the Task.Delay method, but you call method that do intensive work...

<button type="button" @onclick="@PlaceOrder_Clicked" disabled="@DisablePlaceOrderButton">@Title</button>

@code{
    private bool DisablePlaceOrderButton { get; set; } = false;

    public string Title { get; set; } = "Place Order";

    private async Task PlaceOrder_Clicked()
    {

        await DisablePlaceOrder();

        //if (cart.Lines.Count() == 0)
        //{
        DisablePlaceOrderButton = false;
        Title = "Place Order";
        // return;
        //}

    }

    async Task DisablePlaceOrder()
    {
        DisablePlaceOrderButton = true;
        Title = "Wait...";
        await Task.Delay(3000); 

    }
}
enet
  • 41,195
  • 5
  • 76
  • 113
  • 1
    The main part of your code is Task.Delay(), So I didn't create DisablePlaceOrder() method, and used the original PlaceOrder_Clicked() with the StateHasChanged() replaced with: await Task.Delay(1000); and every thing is fine. – mz1378 Jan 05 '20 at 13:06
  • 2
    But this is not right thing to do , right ? because statehaschagned() should re-render component. – Aarsh Mar 05 '20 at 04:38
  • 2
    Indeed. This is hacky. Does anyone know the correct way to do this? MS BOL is not quite explaining this. I'm still scratching my head as to why this doesn't work as expected on a more complex app. External async components are a royal pain - they refresh fine, but once the call is complete and control returned to the caller (main page), it acts as though nothing has changed even when using StateHasChanged(), making a database call, or what-have-you! I've only seen this Task.Delay(1) crap all over S.O. as "the answer". Any experts on Blazor? – MC9000 Jan 27 '23 at 01:31