I have a button and a message that should be shown if condition is true.
<button @onclick="Helper.CallFunc">Call async func</button>
@if(Helper.Loading)
{
@("Loading...")
}
LoadingHelper
class look like this
public class LoadingHelper
{
public bool Loading { get; private set; } = false;
public Func<Task> PassedFunc { private get; set; } = async () => await Task.Delay(2000);
public Func<Task> CallFunc => async () =>
{
Loading = true;
await PassedFunc();
Loading = false;
};
}
When I define Helper
object like this the message is indeed shown for 2000 miliseconds
LoadingHelper Helper { get; set; } = new()
{
PassedFunc = async () => await Task.Delay(2000)
};
However when it's defined like this the message is never shown
LoadingHelper Helper => new()
{
PassedFunc = async () => await Task.Delay(2000)
};
I'm a little bit confused here as to why the Loading
change is not shown in second example. Shouldn't the change be visible regardless if the Helper
object is set with getter
and setter
or only getter
since I'm not modifying the Loading
property directly?
Edit When it's defined like this for some reason it works
LoadingHelper Helper { get; } = new()
{
PassedFunc = async () => await Task.Delay(2000)
};
CodePudding user response:
I should've had researched a bit more before asking a question. The answer is pretty simple as explained here.
Basically LoadingHelper Helper => new()
returned a new object which obviously had Loading
set to false
.
LoadingHelper Helper { get; } = new()
on the other hand returned the same object every time.
CodePudding user response:
Building an anonymous function every time you invoke the Func
is expensive.
public Func<Task> CallFunc => async () =>
{
Loading = true;
await PassedFunc();
Loading = false;
};
You can cache the function like this:
public Func<Task> CallFunc;
public LoadingHelper()
{
CallFunc = async () =>
{
Loading = true;
await PassedFunc();
Loading = false;
};
}
If you want to apply the "loader" to all UI events in a component you can create a custom IHandleEvent.HandleEventAsync
that overloads the standard ComponentBase
implementation. Here's a page that demonstrates how to implement one. This only updates loading
if it's a MouseEventArgs
event.
@page "/"
@implements IHandleEvent
<h3>Loader Demo</h3>
<div >
<button @onclick=HandleClickAsync>Call async func</button>
<button @onclick=HandleClickAsync>Call async func</button>
</div>
<div >
<input type="checkbox" @onchange=HandleCheckAsync />
</div>
@if (this.Loading)
{
<div >Loading... </div>
}
@code {
protected bool Loading;
private async Task HandleClickAsync()
=> await Task.Delay(2000);
private async Task HandleCheckAsync()
=> await Task.Delay(2000);
async Task IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, object? arg)
{
if (arg is MouseEventArgs)
Loading = true;
var task = callback.InvokeAsync(arg);
var shouldAwaitTask = task.Status != TaskStatus.RanToCompletion &&
task.Status != TaskStatus.Canceled;
StateHasChanged();
await task;
if (arg is MouseEventArgs)
Loading = false;
StateHasChanged();
}
}