I created a .razor Notification Component in Blazor, and I'm trying to autoclose the notification div after xx seconds.
So far it works with this Method
private async Task CloseToast(Guid Id, bool autoclose = false)
{
if (autoclose)
{
await Task.Delay(TimeSpan.FromSeconds(5));
}
//Code to remove the notification from list
StateHasChanged();
}
The problem is that for 5 seconds the UI data binding is stuck, any one way or two way binding update to variables (text fields etc..) is on hold until the Notification is closed and the Task resumes.
How can I launch a method or code block after xx seconds without blocking the main UI task in Blazor?
A component with a timer that counts back
<h3>#Time</h3>
#code {
[Parameter] public int Time { get; set; } = 5;
public async void StartTimerAsync()
{
while (Time > 0)
{
Time--;
StateHasChanged();
await Task.Delay(1000);
}
}
protected override void OnInitialized()
=> StartTimerAsync();
}
Usage:
<Component />
<Component Time="7"/>
Tested on client side Blazor. Should behave the same way in server-side Blazor.
Hope this helps
You can use .NET Timer from System.Timers as well and set the Delay in milisec. When it elapsed event will triggered and you can put your logic into the event handler. If you don't want to bother with all the config and Disposing of Timer you can use this Nuget package. It is a very convenient wrapper for the Timer with many extra features see docs.
<AdvancedTimer Occurring="Times.Once()" IntervalInMilisec="#_closeInMs" AutoStart="true" OnIntervalElapsed="#(e => { IsVisible = false; })" />
#code {
private int _closeInMs = 5000;
...
}
The official Blazor Server EFCore sample project includes this as an example, in TextFilter.razor. The essence of the code is:
Timer? timer;
// ... code in a function to start the timer
timer?.Dispose();
timer = new(DebounceMs);
timer.Elapsed += NotifyTimerElapsed;
timer.Enabled = true;
private async void NotifyTimerElapsed(object? sender, ElapsedEventArgs e)
{
timer?.Dispose();
timer = null;
// SomeMethodAsync will need to call StateHasChanged()
InvokeAsync(() => SomeMethodAsync());
}
and a Dispose() function for the page to dispose any timer in progress when user navigates away.
Related
I want get data from db once on OnInitializedAsync. I try to use tableLoading to judue,but it's not work.
protected override async Task OnInitializedAsync()
{
if (tableLoading)
{
return;
}
tableLoading = true;
users = await userService.GetSome(1, userType);
_total = await userService.GetCount(userType);
tableLoading = false;
Console.WriteLine("OnInitializedAsync");
}
This is the official way to solve your problem. You have to persist component state during first load so that your services won't be called second time during second load.
First add <persist-component-state /> tag helper inside your apps body:
<body>
...
<persist-component-state />
</body>
Then inject PersistentComponentState in your component and use like this:
#implements IDisposable
#inject PersistentComponentState ApplicationState
#code {
private IEnumerable<User> _users;
private int _total;
private PersistingComponentStateSubscription _persistingSubscription;
protected override async Task OnInitializedAsync()
{
_persistingSubscription =
ApplicationState.RegisterOnPersisting(PersistState);
if (!ApplicationState.TryTakeFromJson<IEnumerable<User>>("users", out var restoredUsers))
{
_users = await userService.GetSome(1, userType);
}
else
{
_users = restoredUsers;
}
if (!ApplicationState.TryTakeFromJson<int>("total", out var restoredTotal))
{
_total = await userService.GetCount(userType);
}
else
{
_total = restoredTotal;
}
}
private Task PersistState()
{
ApplicationState.PersistAsJson("users", _users);
ApplicationState.PersistAsJson("total", _total);
return Task.CompletedTask;
}
void IDisposable.Dispose()
{
_persistingSubscription.Dispose();
}
}
How i know blazor OnInitializedAsync exec in once or twice?
It usually loads twice.
Once when the component is initially rendered statically as part of the page.
A second time when the browser renders the component.
However, If you want to load it once, in that case, you could go to _Host.cshtml and change render-mode="ServerPrerendered" to render-mode="Server", and it would be called only once as a result it would then load your data from the database once only.
Note: For more information you could refer to the official documents here.
I know it's usually loads twice, i want to know when the function is run, how to konw it's run on once or twice. This is my solution.
static bool first = true;
protected override async Task OnInitializedAsync()
{
if (first)
{
first = false;
Console.WriteLine("first time");
return;
}
Console.WriteLine("second time");
}
I am working on investment tracking app that will be free for everyone. I am using net Core with blazor and Blazorise for charts.
I stumbled upon a problem with rendering the charts. From the official Blazorise documentation I added method protected override async Task OnAfterRenderAsync(bool firstRender) (see in the code below). This method should redraw the charts the first time the page renders. The problem is that this method always fires twice. It renders the charts in the first go and the second time it leaves them empty (as firstRender = false the second time it fires). If I remove the if block the charts render ok.
Furthermore I've added button that should refresh the data + charts. After pressing this button the charts refresh twice (this is unwanted behaviour as it distracts the users) and what is interesting the data itself (the values) change after the second go.
Have anybody dealt with this problem before?
My html code
...
<div class="btn" #onclick="(async () => await RerenderPage())">Refresh Data</div>
...
My code
List<Models.VM.OverView> overview = new List<Models.VM.OverView>();
protected override async Task OnInitializedAsync()
{
overview = await GetOverview(); //gets overview from api
}
public async Task<List<Models.VM.OverView>> GetOverview()
{
return await Http.GetFromJsonAsync<List<Models.VM.OverView>>("/api/Overview/GetOverView/" + await GetUserIdAsync);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await HandleRedraw();
}
}
async Task HandleRedraw()
{
await pieChart.Clear();
//this method goes in the overview object and gets data from it
await pieChart.AddLabelsDatasetsAndUpdate(GetLabelsPieChart(), GetPieChartDataset());
}
What is the await Rerenderpage() doing ?
From the samples it looks like it employs the user of a bool isAlreadyInitialised flag.
protected override async Task OnAfterRenderAsync( bool firstRender )
{
if ( !isAlreadyInitialised )
{
isAlreadyInitialised = true;
await HandleRedraw();
}
}
I am assuming that async () => await RerenderPage()) calls something that then calls the chart.Update in order for the component to know its statuschanged ?
It seems that Blazor makes it easy for the client to call a server's method using something called SignalR underneath. But when I searched if Blazor does that for the other direction, the answer was that Blazor does not do that and I have to implement it myself using SignalR. So, I thought about polling.
That is, I like to read a property or call a method in the server's object at an interval to determine if something has changed. To test this, I added this property to the WeatherForecastService class. The property increases every time when read.
int _Value = 0;
public int Value
{
get
{
_Value++;
return _Value;
}
}
In the weather tablet sample plage, I removed the code to display the weather and added this.
<p>This component demonstrates fetching data from a service.</p>
<div>Value = #this.Value</div>
#code {
private int Value = 0;
protected override async Task OnInitializedAsync()
{
System.Timers.Timer t = new System.Timers.Timer();
t.Elapsed += (s, e) => { Value = ForecastService.Value; };
t.Interval = 1000;
t.Start();
}
}
And it did not work. I found an existing question, but it uses F# which I don't understand, and I don't know what ClientTImer is, so the answer was not helpful to me.
t.Elapsed += async (s, e) =>
{
Value = ForecastService.Value;
await InvokeAsync(StateHasChanged);
};
I am creating an Android activity and starting a service from this activity using the following code. Now I want to display text "Hello user" from this service to screen, meaning service should trigger this display. I could use Toast.maketext.show but the display will disappear after couple of seconds.
new Thread(new Runnable() {
#Override
public void run() {
startAdapterIntent.setAction("START_ADAPTER");
startService(startAdapterIntent);
System.out.println("Thread2: Adapter Service started.");
}
}).start();
How do I do that?
I tried to use this link:
Making changes to Main Activity UI from thread in Service
and its (probably) parent link
http://developer.android.com/reference/android/app/Service.html
section "Remote Messenger Service Sample"
While using this second link, the onServiceConnected part of ServiceConnection doesn't seem to be working? Please help. Thank you.
From within your service you should create a handler that can be triggered by a timer task, the handler should be the one directly responsible of doing the communication with the currently active UI thread..an example would be
long delay = 3000;
long period = 3000;
TimerTask mDoTask = new TimerTask() {
#Override
public void run() {
xHandler.sendMessage(Message.obtain(xHandler, SOME_OPERATION));
}
};
mT.scheduleAtFixedRate(mDoTask, delay, period);
Handler xHandler=new Handler(){
#Override
public void handleMessage(Message message){
switch (message.what){
case SOME_OPERATION:
YourMethod();// in this method you can display your text
break;
}
}
};
I need to register different share charm listener for every page. I have 2 pages. I added following code in every one:
DataTransferManager.GetForCurrentView().DataRequested += App_DataRequested;
I added it in constructor of one page and in UserControl_Loaded event of another (first page just doesn't have UserControl_Loaded so why I added it directly to constructor). At the moment when second page tryting to load, I got exception:
WinRT information: An event handler has already been registered
Additional information: A method was called at an unexpected time.
Where should I place it and what is "right" time to do this??
Also it looks confusing that we have different DataTransferManager for every view, but only one is active at current time. Ever more, I noticed, if you add only one listener for first page, other pages will share this listener anyway. If I have only one shared listener for all pages, is it correct register it in app.xaml.cs?
The way I resolved this issue was to deregister the event in the onNavigatedfrom event as below:
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
DataTransferManager.GetForCurrentView().DataRequested -= App_DataRequested;
base.OnNavigatedFrom(e);
}
In BasePage.cs in constructor I added
public BasePage()
{
if (!_isListenToDataRequested)
{
_isListenToDataRequested = true;
DataTransferManager manager = DataTransferManager.GetForCurrentView();
manager.DataRequested += AppDataRequested;
}
}
private async void AppDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
IShareable shareable = Frame.Content as IShareable;
if (shareable != null)
{
DataRequestDeferral deferral = args.Request.GetDeferral();
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => shareable.AppDataRequested(sender, args));
deferral.Complete();
}
}
And all my pages look like
public sealed partial class ContentPage : IShareable
{
public void AppDataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{...}
}
Another solution was run this as below
private DataTransferManager dataTransferManager;
Put this in page loaded event
this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, new DispatchedHandler(() =>
{
this.dataTransferManager = DataTransferManager.GetForCurrentView();
this.dataTransferManager.DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(this.OnDataRequested);
}));
And
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
// Unregister the current page as a share source.
this.dataTransferManager.DataRequested -=
new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>
(this.OnDataRequested);
}
I'd suggest doing it in the navigating events, the OnNavigatingFrom event will be triggered before the OnNavigatingTo of the page you're going to so you won't have this problem.
protected override Task OnNavigatingTo(WinRTXamlToolkit.Controls.AlternativeNavigationEventArgs e)
{
DataTransferManager.GetForCurrentView().DataRequested += dataTransfer_DataRequested;
return base.OnNavigatingTo(e);
}
protected override Task OnNavigatingFrom(WinRTXamlToolkit.Controls.AlternativeNavigatingCancelEventArgs e)
{
DataTransferManager.GetForCurrentView().DataRequested -= dataTransfer_DataRequested;
return base.OnNavigatingFrom(e);
}
//Note: This is the WinRT Xaml Toolkit version of the events, but the standard events will work the same way.