auto refresh with blazor server

Viewed 1855

I am working on a Blazor Server web application. I have an SQL Server Database and Entity Framework.

Look at this Entity:

public class Room
{
   public int Id {get;set;}
   public String name {get;set;}
   public DateTime closing_date {get;set;}
}

I want to display Room table content on a razor page:

<Virtualize Items="@rooms" Context="room">
    <table border="1">
    <tr @key=room.Id>
        <td>@room.Id</td>
        <td>@room.Name</td>
        <td>Closing in XXX minutes and YYY seconds</td>
    </tr>
    </table>
</Virtualize>

As you can see, i don't want to display the closing date/time. I want to compute how many minutes are remaining before closing.

I can easily calculate XXX and YYY values. This is easy. What i want to do is to refresh values each seconds. How can i do that ?

In a classic web application i would create a javascript timer with setInterval for example. How can i do that in a blazor server project ?

Thanks

2 Answers

Henk is correct in his comment that you have to Dispose the timer and Invoke StateHasChanged. Here is the complete solution with those changes for .NET Core 6.

Razor component:

@using Microsoft.AspNetCore.Components
@implements IDisposable

<p>Time Remaining:  @_counter</p>

@code
{
    private static System.Timers.Timer _timer;
    private int _counter = 60;

    protected override void OnInitialized()
    {
        _timer = new System.Timers.Timer(1000);
        _timer.Elapsed += CountDownTimer;
        _timer.Enabled = true;
    }

    public async void CountDownTimer(Object source, System.Timers.ElapsedEventArgs e)
    {
        if (_counter > 0)
        {
            _counter -= 1;
        }
        else
        {
            _timer.Enabled = false;
        }

        await InvokeAsync(async () =>
        {
            StateHasChanged();
        });
    }

    public void Dispose()
    {
        _timer?.Dispose();
    }
}

And the code to use it on a Razor or MVC page assuming the component page is called "RoomTimer.razor".

 <div class="row">
    <div class="col-md-8">
        <component type="typeof(RoomTimer)" render-mode="ServerPrerendered" />
    </div>
</div>  

@section scripts {
    <script src="_framework/blazor.server.js"></script>
}

You can use the Timer class underneath the System.Timers namespace in order to accomplish this.

See the example here.

@code {
    private static System.Timers.Timer _timer;
    private int counter = 60;

    protected override void OnInitialized()
    {
        StartTimer();
    }

    public void StartTimer()
    {
        _timer = new System.Timers.Timer(1000);
        _timer.Elapsed += CountDownTimer;
        _timer.Enabled = true;
    }

    public void CountDownTimer(Object source, System.Timers.ElapsedEventArgs e)
    {
        if (counter > 0)
        {
            counter -= 1;
        }
        else
        {
            _timer.Enabled = false;
        }

        StateHasChanged();
    }
}

You can combine this with something like Humanizer to easily get human-friendly formatting.

You'll want to make a component that is a <tr> element which conveniently I've answered elsewhere...

Note that if you have thousands of these running per second, there are going to be some performance considerations. Whether that be reducing the refresh rate or continuing a Blazor Server solution over a JavaScript solution.

Related