modal-dialog.net-6.0blazor-server-side

Loading Modal does not show until method is completed in Blazor Server


I need to show a Loading modal in Blazor server, using Net6. When finish loading, it needs to change the text "loading..." with "finished".

But it only opens when everything is finished

I modified counter.razor from the example project so it takes 5 seconds of loading to Increment the counter:

@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
<Modal @ref="modal">
    <Title>This is a <em>Title!</em></Title>
    <Body>
        @if (loading)
        {
            <p>
                Incrementing please wait...
            </p>
        }
        else
        {
            <p>
                Ready
            </p>
        }
    </Body>
    <Footer>
        <button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="() => modal.Close()">Close</button>
    </Footer>
</Modal>
@code {
    private Modal modal;
    private int currentCount = 0;
    private bool loading;
    private async void IncrementCount()
    {
        loading = true;
        modal.Open();
        StateHasChanged();
        Task.Delay(5000).Wait();
        currentCount++;
        loading = false;
    }
}

Modal.razor:

<div class="modal @modalClass" tabindex="-1" role="dialog" style="display:@modalDisplay; overflow-y: auto;">
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">@Title</h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                @Body
            </div>
            <div class="modal-footer">
                @Footer
            </div>
        </div>
    </div>
</div>

@if (showBackdrop)
{
    <div class="modal-backdrop fade show"></div>
}

@code {
    [Parameter]
    public RenderFragment Title { get; set; }

    [Parameter]
    public RenderFragment Body { get; set; }

    [Parameter]
    public RenderFragment Footer { get; set; }

    private string modalDisplay = "none;";
    private string modalClass = "";
    private bool showBackdrop = false;

    public async void Open()
    {
        modalDisplay = "block;";
        modalClass = "show";
        showBackdrop = true;
    }

    public void Close()
    {
        modalDisplay = "none";
        modalClass = "";
        showBackdrop = false;
    }
}

It only opens showing "Ready" AFTER the 5 seconds delay. I tried using another thread for opening the modal, using Task.Run, but it has the same result

Solved by Laurence Frost comment

Other workaround I found was this

    private async void IncrementCount()
{
 
    await Task.Run(()=>{
        loading = true;
        modal.Open();
        InvokeAsync(StateHasChanged);
    });
    Task.Delay(2000).Wait();
    currentCount++;
    loading = false;
    StateHasChanged();
    
}

Solution

  • One potential reason might be because you are using async void as opposed to async Task in your methods.

    Try changing your IncrementCount method as follows:

    private async Task IncrementCount()
    {
        loading = true;
        modal.Open();
        StateHasChanged();
        await Task.Delay(5000);
        currentCount++;
        loading = false;
    }
    

    I would also change other async methods so that they return Task instead of void. You can read up on the specifics of why here:

    https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming#avoid-async-void