Blazor - Display Wait or Spinner on API Call

Blazor - Display wait or spinner on API call


Option 1: Using Task.Delay(1)

  • Use an async method.
  • Use await Task.Delay(1) or await Task.Yield(); to flush changes
private async Task AsyncLongFunc()    // this is an async task
{
spinning=true;
await Task.Delay(1); // flushing changes. The trick!!
LongFunc(); // non-async code
currentCount++;
spinning=false;
await Task.Delay(1); // changes are flushed again
}

Option 1 is a simple solution that runs ok but looks like a trick.

Option 2: Using Task.Run() (not for WebAssembly)

On January'2020. @Ed Charbeneau published BlazorPro.Spinkit project enclosing long processes into task to don't block the thread:

Ensure your LongOperation() is a Task, if it is not, enclose it into a Task and await for it:

async Task AsyncLongOperation()    // this is an async task
{
spinning=true;
await Task.Run(()=> LongOperation()); //<--here!
currentCount++;
spinning=false;
}

Effect

a spinner loading data

Spinner and server side prerendering

Because Blazor Server apps use pre-rendering the spinner will not appear, to show the spinner the long operation must be done in OnAfterRender.

Use OnAfterRenderAsync over OnInitializeAsync to avoid a delayed server-side rendering

    // Don't do this
//protected override async Task OnInitializedAsync()
//{
// await LongOperation();
//}

protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await Task.Run(()=> LongOperation());//<--or Task.Delay(0) without Task.Run
StateHasChanged();
}
}

More samples

Learn more about how to write nice spinner you can learn from open source project BlazorPro.Spinkit, it contains clever samples.

More Info

See Henk Holterman's answer with blazor internals explanation.

C# Blazor show spinner until foreach loop is finished

Working Blazor Demo

I have created a working Blazor Fiddle which you can use as a guide here. It simulates a long running data query and displays a loading icon while running.

The main problem with your code is that the if(isLoading)condition is inside the loop building the table. These need to be reversed by moving the if statement outside the loop. It will then correctly either show the loading icon or show the table depending on the value of isLoading.

So instead of this logic:

loop over data {
if loading
show icon
else
show table rows
end
}

You want it to look like this:

if (loading) 
show icon
else
loop over data {
show table rows
}
end

The other change I would suggest is to load your data in either OnInitializedAsync() or OnAfterRenderAsync(). These are both async and you can "await" your data without the need for an extraneous Dispatcher. Be aware that Blazor server calls the initialization twice due to pre-rendering, which you probably want to avoid if you have a long running query. Yet, the solution to that is outside the scope of the question.

Blazor loading animation for API vs. EF

I'm not sure why it works the first time but Sleep() blocks the main thread and therefore the required update rendering.

//Thread.Sleep(5000);
await Task.Delay(5000);

blazor loading spinner when component is loading html/css

You need StateHasChanged for intermediate updates. Blazor applies it before and after an event, but that doesn't help you here.

<button @onclick="@DoLogic">Load</button>
async Task DoLogic()
{
edit = false; // means loading == true
StateHasChanged(); // to be sure, probably not needed
await Task.Delay(1); // allow time for the rendering

// do the actual loading

edit = true; // means loading == false
}

The trick here is await Task.Delay(1); so your method is suspended for a bit and Blazor can update the DOM.

There is no reason for InvokeAsync here.

Why does the spinner not stop straight away and show the message pop up?

Becuase you're running updates to fields in an asynchronous method, there's a chance that when you change those values such as the message text - that the state isn't being updated on the page and the text and is not showing.

Try using await InvokeAsync(()=>StateHasChanged()); to tell the UI that it should update with new values.



Related Topics



Leave a reply



Submit