I'm working on an ASP.Net Core 6 project and I am trying to add a selection function to the rows of my table. My problem is that my selection function requires me to have access to the index of the selected object and I can't do that with IEnumerable. So, I'm wondering how I can change my code to bind my view to a list instead. I currently have a viewModel that contains properties that are populated from two different tables in the database that I need for my list.
EventListViewModel.cs
public class EventListViewModel
{
//from Events table
public int EventId { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string Type { get; set; }
//from Clients table
public int ClientId {get ; set; }
public string ContactName { get; set; }
public string Phone { get; set; }
}
EventController.cs (index action)
public IActionResult Index()
{
var result = from e in _db.Events
join c in _db.Clients on e.ClientId equals c.Id
select new EventListViewModel
{
EventId = e.Id,
StartDate = e.EventStart,
EndDate = e.EventEnd,
Type = e.Type,
ClientId = c.Id;
ContactName = c.Name,
Phone = c.Phone
}; //.ToList();
//.ToList() doesn't work here
return View(result);
}
In my Event/Index.cshtml I am binding the model as an IEnumerable, but I need it to be a list, so I can iterate through it.
@model IEnumerable<MyProject.ViewModels.EventListViewModel>
Event/Index.cshtml table looks like this
<tbody>
@foreach (var item in Model)
{
int index = Model.IndexOf(item);
//above line gives error IEnumerable does not contain a definition for IndexOf
<tr class="table-light selectable" controller="Event" Action="Selection" data-
id="@item.Id" data-index="@index">
<td>@item.Type </td>
<td>@item.StartDate.ToShortDateString()</td>
<td>@item.StartDate.TimeOfDay</td>
<td>@item.EndDate.TimeOfDay</td>
<td>@item.ContactName</td>
<td>@item.Phone</td>
</tr>
</tbody>
And here is the .js function for class selectable that needs to be able to get the item index
$((function () {
$(".selectable").on('click', (e) => {
e.preventDefault
var selectedRow = e.target.parentElement;
var id = $(selectedRow).data('id');
var index = $(selectedRow).data('index');
var controller = selectedRow.getAttribute("controller");
var action = selectedRow.getAttribute("action");
var url = "/" + controller + "/" + action + "/" + id;
var trArray = selectedRow.parentElement.getElementsByTagName('tr');
for (var row = 0; row < trArray.length; row++) {
if (row == index)
{
tdArray = selectedRow.getElementsByTagName('td');
for (var cell = 0; cell < tdArray.length; cell++)
{
tdArray[cell].style.backgroundColor = "yellow";
//need to remove hover for these cells
}
}
else
{
tdArray = trArray[row].getElementsByTagName('td');
for (var cell = 0; cell < tdArray.length; cell++)
{
tdArray[cell].style.backgroundColor = "#f8f9fa";
//need to add hover back into these cells
}
}
}
$.ajax({
url: url,
type: "POST",
success: function (response) {
$(".selectionResult").val(response);
$(".selectedId").val(id);
}
})
});
}()));
I'm pretty sure I can accomplish this by creating another ViewModel that contains just the list, and bind the cshtml file to that, but I'm wondering if there is a more elegant way of doing this, or if there is possibly some other way to write my selectable function to not require the index. Any help would be much appreciated.
This actually ended up being pretty easy to fix. I saw a lot of way more complicated answers, so I thought I'd share mine for anyone else who is having a similar issue. Basically, I just initialized an index to -1 and added it into my loop. (EventList is type IEnumerable).
<tbody>
@if (Model.EventList != null)
{
int index = -1;
@foreach (var item in Model.EventList)
{
index++;
<tr class="table-light selectable" controller="Event"
Action="Selection" data-id="@item.EventId" data-index="@index">
<td>@item.Type </td>
...
</tr>
}
}
</tbody>