Reputation: 169
I have a situation where I have a for loop that creates my html table from my datamodel which gets the data from SQL server express. I would like to know if it is possible to create a auto refresh method where the table data only gets refreshed and not the full page, if not then maybe a method that OnClick button will retrieve the latest data from datamodel and update the table accordingly.
I'm new to blazor and C# so any help would be appreciated, my current page structure currently looks as follows:
@page "/employees"
@using DataLib;
@inject IEmployeeData _db
@if (employees is null)
{
<p style="color:white;"><em>Loading . . .</em></p>
}
else
{
<table class="table" id="myTable">
<thead>
<tr>
<th>Entry Date</th>
<th>Employee</th>
</tr>
</thead>
<tbody>
@foreach (var employee in employees)
{
<tr>
<td>@employee.EntryDate</td>
<td>@employee.POI</td>
</tr>
}
</tbody>
</table>
}
@code{
private List<EmployeeModel> employees;
protected override async Task OnInitializedAsync()
{
employees = await _db.GetEmployee();
}
}
The above works perfect when I'm loading this page and when I do a manual refresh.
Is there a way that you guys can maybe assist me?
Thanks.
Upvotes: 4
Views: 16268
Reputation: 1
I have tried the response above (answered Jun 10, 2020 at 15:42) and there's a few issues with it:
if it's called like that then there will be an infinite loop of calls and button clicks, because the code in
protected override async Task OnAfterRenderAsync(bool firstRender) { await JSRuntime.InvokeVoidAsync("EmployeeInterop.refreshEmployeeData"); }
will be called for all component render events which will happen every time the button is clicked.
The code has to be like this:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender) {
await JSRuntime.InvokeVoidAsync("EmployeeInterop.refreshEmployeeData");
}
await base.OnAfterRenderAsync(firstRender);
}
Upvotes: 0
Reputation: 3216
If your aim is just to refresh the data at regular interval then you can make use of Javascript Interop that is supported by Blazor. The set-up documentation is available in this link.
Like said by Mathias Z in this solution you would need a button for this to work.
I can see you have been writing both C# code and HTML in same file however I personally prefer keeping them separately. Coming back to the solution you can make use to JavaScript and c# to periodically refresh your displayable content.
Below are the changes you need to make to make it work.
Code-Behind
using Microsoft.JSInterop; // import this library
using this you can invoke JavaScript methods.
[Inject]
private IJSRuntime JSRuntime { get; set; }
OnAfterRenderAsync and OnAfterRender are called after a component has finished rendering. Element and component references are populated at this point. Use this stage to perform additional initialization steps using the rendered content, such as activating third-party JavaScript libraries that operate on the rendered DOM elements.
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await JSRuntime.InvokeVoidAsync("EmployeeInterop.refreshEmployeeData");
}
// This method will be called on button click.
protected async Task GetEmployees()
{
employees = await _db.GetEmployee();
}
wwwroot
Within this folder we generally keep our web-resources including js libraries. Here, create a javascript file e.g. EmployeeInterop.js and below code.
(function () {
window.EmployeeInterop = {
refreshEmployeeData: () => {
setInterval(() => {
document.getElementById("btnGetEmployeeData").click();
}, 3000);
}
};
})();
The setInterval() method calls a function or evaluates an expression at specified intervals (in milliseconds). You can define your own time of refresh.
_Host.cshtml
Register this script file here.
<script src="EmployeeInterop.js"></script>
EmployeeDetail.razor
<button id="btnGetEmployeeData" @onclick="GetEmployees"> Refresh Employee List</button>
Add this below your table tag. In-case you don't want this button to be visible to the end-user then add a style attribute and set it to display:none
.
Upvotes: 0
Reputation: 11
Mathias Z
I not understand why not this answer is not taken for good, but for me is all that i want, StateHasChanged(); because i still not use JavaScript.
public MyConstructor()
{
_My_collection_.CollectionChanged += Change_EventArgs;
}
void Change_EventArgs(object sender, EventArgs e)
{
StateHasChanged();
}
Upvotes: 1
Reputation: 14623
You could create a SignalR hub on your server. Inject the hub into your api controllers, use it to signal clients that updates have occurred to the data from the API.
Upvotes: 1
Reputation: 575
Not sure this is your aim butt you could try;
@inject IEmployeeData _db
@if (employees is null)
{
<p style="color:white;"><em>Loading . . .</em></p>
}
else
{
<table class="table" id="myTable">
<thead>
<tr>
<th>Entry Date</th>
<th>Employee</th>
</tr>
</thead>
<tbody>
@foreach (var employee in employees)
{
<tr>
<td>@employee.EntryDate</td>
<td>@employee.POI</td>
</tr>
}
</tbody>
</table>
<button @onclick="GetEmployees"> Refresh Employee List</button>
}
@code{
private List<EmployeeModel> employees;
protected override async Task OnInitializedAsync()
{
GetEmployees()
}
private async void GetEmployees()
{
employees.Clear();
employees = await _db.GetEmployee();
StateHasChanged();
}
Good luck,
Upvotes: 5