In my blazor, I put:
@if (cat.IsMeowing)
{
<div>Cat is meowing!!!</div>
}
In my Cat
class, I put this:
public bool IsMeowing {get; set;} = false;
public void Meow()
{
Task.Run(async () =>
{
await Task.Delay(3000); // Cat takes a deep breath
IsMeowing = true; // MEOW!!!!!!!
});
}
The behaviour I want is that when I call Meow, there is a delay, and then it sets the variable which causes the Div to appear. However, blazor does not seem to notice that the variable has updated when it is updated via a closure from another thread.
If I remove the task, and just put the delay and the IsMeowing=true, then blazor notices it and updates correctly.
Is there a way I can get around this without implementing a callback?
Ultimately, I want to create a class that when a method is invoked on it, sets a variable after 3 seconds that blazor notices. Imagine for example that I want to show a message "This operation is taking a while..." if 3 seconds elapses while I'm doing other intensive work, so I can set one of these going, start doing my intensive work, and after my intensive work is done cancel it. If the intensive work took <3 seconds nothing would happen, and if it took more than 3 seconds the message would appear via the blazor if.
CodePudding user response:
The task you create is something external, hence this applies. This is one of the cases when StateHasChanged() must be called. To do that:
Main.razor:
@implements IDisposable
@if (cat.IsMeowing)
{
<div>Cat is meowing!!!</div>
}
<button @onclick="() => cat.Meow()" title="Poke the cat">Poke the cat</button>
@code {
public Feline cat = new Feline();
protected override void OnInitialized()
{
cat.UpdateState = StateHasChanged;
}
public void Dispose()
{
cat.UpdateState -= StateHasChanged;
}
}
Feline.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class Feline
{
public Action UpdateState = null;
public bool IsMeowing {get; set;} = false;
public void Meow()
{
Task.Run(async () =>
{
await Task.Delay(3000); // Cat takes a deep breath
IsMeowing = true; // MEOW!!!!!!!
if(UpdateState != null)
UpdateState();
});
}
}
Example: https://blazorrepl.telerik.com/QmbcnYvw45hRuXES33
Correct me if I am wrong.
CodePudding user response:
Blazor does not 'detect variable change' at all.
But you can easily use the async rendering logic:
//public void Meow()
public Task Meow()
{
Task t1 = ShowMeow();
Task t2 = longRunningTask(); // or t2 = Task.Run(longRunningTask)
await Task.WhenAll(t1, t2);
}
private Task ShowMeow()
{
await Task.Delay(3000); // Cat takes a deep breath
IsMeowing = true; // MEOW!!!!!!!
}
When longRunningTask() executes (mostly) synchronous then use Task.Run(), on Blazor-server only.