Refresh html table data using Blazor and C# - c#

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.

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,

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.

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();
}

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.

Related

Blazor navigation from within a loop?

So I'm trying to make a Blazor application that displays a customers data. When all the customers are displayed I would like to be able to navigate to an individual customer. I am having trouble making this work. The main concept is in the code below as well I have the expected model.
#for(int i = 0; i < customers.Length-1; i++)
{
<tr>
<td>#customer.Id</td>
<td>#customer.CustomerId</td>
<td>#customer.LastName</td>
<td>#customer.FirstName</td>
<td>#customer.Name1056Form</td>
<td>#customer.TrapezeClientId</td>
<td>#customer.CustomerNote</td>
<td #onclick="NavToCustomer(i)"> Details</td>
</tr>
private void NavToCustomer(int i)
{
int Id = i;
navigation.NavigateTo("/Customer/" + Id);
}
}
<td #onclick="()=>NavToCustomer(#customer.Id)"> Details</td>
private void NavToCustomer(int custID)
{
navigation.NavigateTo("/Customer/" + custID);
}
Explanation: the vanilla #onclick sends its own arguments (you can see what they are by hovering over "#onclick" in your markup). Your event handler doesn't handle MouseEventArgs, so the signatures don't match. You want to send custom info, so you should use the lambda expression instead.
--edit--
On second thought, can you just directly do this?
<td #onclick="()=>navigation.NavigateTo("/Customer/" + customer.Id)"> Details</td>

Scroll to specified part of page when clicking top navigation link in Blazor

How can I make a simple "jump to" part of already loaded page in Blazor? Like this in HTML:
Contact us
...
<section id="contact">
Ideally I also want to have it smooth scroll down to this section. Thought I would try to solve this with CSS, but maybe not possible?
I've solved this by using a button and then writing some inline Javascript in the markup. You can generalize this to a Blazor component for bonus points!
<button type="button" onclick="document.getElementById('contact').scrollIntoView({behavior:'smooth'})">Contact us</button>
...
<section id="contact">
What you need is the hashed routes features of Blazor. But, alas, no such features do exist yet. I'd suggest you use JSIterop to perform this task: Create a JavaScript that performs the navigation, and pass it an ElementRef object.
Hope this helps...
Edit: The following is an adaptation of the best workaround solution I've found in Github...
Ordinarily, when you click the link to contact, you get redirected to the route http://localhost:5000/mypage#contact, but will be at the top of the page. The fragment of the route is not used for selection of a specific HTML element.
The current workaround is to write explicit code that interprets the URL. In the example above, we could use a little bit of JavaScript and then call that from our Blazor code:
mypage.cshtml:
#page "/mypage"
#inject Microsoft.AspNetCore.Components.Services.IUriHelper UriHelper
<nav>
contact
</nav>
<section>
<h2 id="contact">contact</h2>
</section>
#functions {
protected override void OnInit()
{
NavigateToElement();
UriHelper.OnLocationChanged += OnLocationChanges;
}
private void OnLocationChanges(object sender, string location) => NavigateToElement();
private void NavigateToElement()
{
var url = UriHelper.GetAbsoluteUri();
var fragment = new Uri(url).Fragment;
if(string.IsNullOrEmpty(fragment))
{
return;
}
var elementId = fragment.StartsWith("#") ? fragment.Substring(1) : fragment;
if(string.IsNullOrEmpty(elementId))
{
return;
}
ScrollToElementId(elementId);
}
private static bool ScrollToElementId(string elementId)
{
return JSRuntime.Current.InvokeAsync<bool>("scrollToElementId", elementId).GetAwaiter().GetResult();
}
}
index.html:
<script>
window.scrollToElementId = (elementId) => {
console.info('scrolling to element', elementId);
var element = document.getElementById(elementId);
if(!element)
{
console.warn('element was not found', elementId);
return false;
}
element.scrollIntoView();
return true;
}
</script>
Note: If you're using Blazor version .9.0, you should inject the IJSRuntime
Please, let me know if this solution works for you...
You can use 2 Nuget packages to solve this:
Scroll JS which is part of JsInterop Nuget. This has many feature see the docs but main part is IScrollHandler which is an injectable service. You can try it out with the demo app. You can see the scrollIntoView() and other JS functions wrapped, smooth scroll available. So much simpler to use JS scroll support...
Second option is to use "Parmalinks" in the URL you have "#". Nuget available here. It is what you requested. Basically it is using <a> tags but you don't have to bother with it (note even demo app has # URLs). Also renders "link" component with clickable icon and navigate/copy actions. Currenlty smooth scroll is not available but can be requested. You can try it out with the demo app.
I used Arron Hudon's answer and it still didn't work. However, after playing around I realized it wouldn't work with an anchor element: <a id='star_wars'>Place to jump to</a>. Apparently Blazor and other spa frameworks have issues jumping to anchors on the same page. To get around that I had to use a paragraph element instead (section would work too): <p id='star_wars'>Some paragraph<p>.
Example using bootstrap:
<button class="btn btn-link" onclick="document.getElementById('star_wars').scrollIntoView({behavior:'smooth'})">Star Wars</button>
... lots of other text
<p id="star_wars">Star Wars is an American epic...</p>
Notice I used bootstrap's btn-link class to make the button look like a hyperlink.
The same answer as the Issac's answer, but need to change some code.
I found the main problem is that you need it to be async. #johajan
#inject IJSRuntime JSRuntime
...
#functions {
protected override async Task OnInitAsync()
{
await base.OnInitAsync();
//NavigateToElement();
UriHelper.OnLocationChanged += OnLocationChanges;
}
private async Task OnLocationChanges(object sender, string location) => await NavigateToElement();
private async Task NavigateToElement()
{
var url = UriHelper.GetAbsoluteUri();
var fragment = new Uri(url).Fragment;
if(string.IsNullOrEmpty(fragment))
{
return;
}
var elementId = fragment.StartsWith("#") ? fragment.Substring(1) : fragment;
if(string.IsNullOrEmpty(elementId))
{
return;
}
await ScrollToElementId(elementId);
}
private Task<bool> ScrollToElementId(string elementId)
{
return JSRuntime.InvokeAsync<bool>("scrollToElementId", elementId);
}
}

Bind jQuery datatable values to controller parameter

I have a controller which returns a view model that has a List<string> property, and displays the results in series of text boxes in a jQuery datatable. I want to allow the user to edit the list, and post the updated data back to the controller. This is working correctly if I stay on the first page of the data table, but if I navigate to the next page and then edit one of the fields, the List<string> property on the view model is an empty list in the controller action.
Here's my view model
public class ViewModel
{
public List<string> Values { get; set; }
public ViewModel()
{
this.Values = new List<string>();
}
}
My controller actions
public ActionResult Edit()
{
ViewModel viewModel = new ViewModel();
viewModel.Values.Add("ST0001");
viewModel.Values.Add("ST0002");
viewModel.Values.Add("ST0003");
viewModel.Values.Add("ST0004");
viewModel.Values.Add("ST0005");
viewModel.Values.Add("ST0006");
viewModel.Values.Add("ST0007");
viewModel.Values.Add("ST0008");
viewModel.Values.Add("ST0009");
viewModel.Values.Add("ST0010");
return View(viewModel);
}
[HttpPost]
public ActionResult Edit(ViewModel viewModel)
{
// I have a breakpoint here to examine the values of viewModel.Values
// As long as I stay on the first page of the data table, viewModel.Values contains the updated values I type
// If I go to the second page of the table, viewModels.Values is an empty list
return RedirectToAction("Edit", viewModel);
}
Here's the datatable in the view
<table id="myTable" class="table">
<thead>
<tr>
<th>
#Html.Label("Value")
</th>
</tr>
</thead>
<tbody>
#if (Model != null)
{
for (int i = 0; i < Model.Values.Count; i++)
{
<tr>
<td>
#Html.TextBoxFor(model => Model.Values[i])
</td>
</tr>
}
}
</tbody>
</table>
#section Scripts
{
<script type="text/javascript">
$(function () {
$('#myTable').dataTable({
'order': [[0, 'asc']],
'pageLength': 5
});
});
</script>
}
The <input>'s id looks correct on each page of the datatable.
Here is what an input control on the first page looks like
Here is what an input control on the second page looks like
I'm assuming the problem is that ASP won't load a List parameter to a controller if the submitted data starts from a non-zero index? Is there any way of getting around this, such as looking at all submitted form data from my controller and manually loading each array element into the appropriate position in my list?

Get HTML check box to toggle MVC model boolean

I am trying to use an MVC form to modify some information in my database. I want to be able to select a few items from a table using a series check boxes. It should update the database boolean values when I hit a link at the bottom of my form.
So far, I have tried a few solutions from other threads, but since I am new to MVCs, they are rather confusing.
This is what I have right now for my HTML:
#foreach (var item in Model)
{
<tr>
#if (!item.IsCurated)
{
<td>
#Html.CheckBoxFor(modelItem => item.isChecked, new { #checked = true })
</td>
{
</tr>
#Html.ActionLink("Update", "updateDatabase", Model)
The "updateDatabase" method calls
public void updateDatabase()
{
db.SaveChanges();
}
I believe the changes to the database are being saved, but that the check boxes are not actually assigning any changed values.

Generate jQuery functions dynamically mvc 4 razor

Currently, I am passing a list of object from controller to a view, and generate labels by the object's name.
What I am trying to do is to generate a jQuery function that will Dynamically create functions (toggle a form with relative lable id) for each label after being clicked.
The jQuery function is not working, I could not output the corrent jQuery function in the webpage...... Could you give me soem hints?
<table>
#foreach (var item in Model)
{
<tr>
<td>
#Html.Label(item.productName, new { #id = item.productId})
</td>
</tr>
}
</table>
<script type="text/javascript">
$(document).ready(function () {
#foreach (var item in Model)
{
$(#item.productId).click)(function({
//do something
}));
}
});
</script>
Thanks very much!
your JS syntax is wrong, for starters. What you want to do is to give all those labels a class name (such as product_lbl) or a data attribute (if you don't like semantic class names) such as product-lbl. This way you don't have to do a second loop to add click event handlers. You'll only need one, like so:
$('.product_lbl').on(
'click',
function() { /* Do something for whichever label was clicked */ }
);
OR
$('[product-lbl]').on(
'click',
function() { /* Do something for whichever label was clicked */ }
);

Categories