I am working on a table component for a website I am making. I want to be able to Create, Update, Add, and Delete entries from the table, all on one page. I will also be using this component all over the website, and with many kinds of classes as the input. I went to here to learn how to do that. I also figured it would be smart to have an internal component that is each row in the table. Here is my code so far:
Editable Class.Razor
@using WbotV2.Data
@using System
@using System.Collections
@using System.Reflection
@typeparam TItem
<tr>
@foreach (var datum in Data)
{
<td>@datum</td>
}
<td><button class="btn btn-success">Edit</button></td>
<td>@ChildContent</td>
</tr>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public TItem Item { get; set; }
public List<string> Data;
public bool Editable = false;
protected override async Task OnInitializedAsync()
{
Data = GetData(Item);
}
public static List<string> GetData<T>(T instance)
{
List<string> ret = new List<string>();
FieldInfo[] infos = typeof(T).GetFields();
foreach (FieldInfo info in infos)
{
ret.Add(info.GetValue(instance).ToString());
}
return ret;
}
}
This component should allow me to pass a class in as a parameter, and then edit it's fields. It should also not take the user to a separate page to edit it (if possible, I'd love to have it inline in the table). My Idea was to use bindings to do this, but the class names are arbitrary so I don't know how to pass that to @bind, which is where I'm stuck.
Table Component.razor
@using WbotV2.Data
@using System
@using System.Collections
@using System.Reflection
@typeparam TItem
<table class="table">
<thead>
<tr>
@foreach (string header in Headers)
{
<th>@header</th>
}
<td>Edit</td>
<td>Delete</td>
<td><button class="btn btn-primary">Add</button></td>
</tr>
</thead>
<tbody>
@foreach (var item in Items)
{
<EditableClass Item="@item"><button class="btn btn-danger" @onclick="(Delete(item))">Delete</button></EditableClass>
}
</tbody>
</table>
@code {
[Parameter]
public IList<TItem> Items { get; set; }
[Parameter]
public TableUtilityClass<TItem> DataInstance { get; set; }
public string[] Headers;
protected override async Task OnInitializedAsync()
{
Headers = GetHeaders();
}
public string[] GetHeaders()
{
List<string> ret = new List<string>();
FieldInfo[] infos = typeof(TItem).GetFields();
foreach (FieldInfo info in infos)
{
ret.Add(info.Name);
}
return ret.ToArray();
}
public void Delete(TItem itemToDelete)
{
DataInstance.Delete(itemToDelete);
StateHasChanged();
Console.WriteLine("Here");
}
public void Add(TItem itemToAdd)
{
DataInstance.Add(itemToAdd);
StateHasChanged();
Console.WriteLine("Here");
}
public List<List<string>> GetDataString<T>(IEnumerable<T> instance)
{
List<List<string>> ret = new List<List<string>>();
foreach (T item in instance)
{
ret.Add(new List<string>());
FieldInfo[] infos = typeof(T).GetFields();
foreach (FieldInfo info in infos)
{
ret[ret.Count - 1].Add(info.GetValue(item).ToString());
Console.WriteLine($"{info.Name}: {info.GetValue(item)}");
}
}
return ret;
}
}
This component should lots of the editable class components in a table and handle the addition and deletion of new entries to it. My current issue here is with the deletion, I have tried implementing this solution but I get a cannot convert from void to Microsoft.AspNetCore.Components.EventCallback error on this line <EditableClass Item="@item"><button class="btn btn-danger" @onclick="(Delete(item))">Delete</button></EditableClass>
I also have tried this and this. I would love some help here too, either resolving the error or finding a clever way around it.
Table Utility Class.cs
using System.Threading.Tasks;
namespace WbotV2.Data
{
public abstract class TableUtilityClass<T>
{
public abstract Task Delete(T Item);
public abstract Task Add(T Item);
}
}
This class should just help make it easy to interact with many different kinds of services, as I anticipate having many.
Finally, Here is my Countries.razor (The page the table component goes in)
@page "/countries"
@using WbotV2.Data
@inject WbotCountryService CountryService
<h1>Country Information</h1>
@if (countries == null)
{
<p><em>Loading...</em></p>
}
else
{
<TableComponent Items="@countries" DataInstance="@CountryService"/>
}
@code {
public List<WbotCountry> countries;
protected override async Task OnInitializedAsync()
{
countries = await CountryService.GetCountriesAsync();
}
}
for those wondering the Country service looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WbotV2.Data
{
public class WbotCountryService : TableUtilityClass<WbotCountry>
{
public WbotData WbotDataInstance;
public WbotCountryService()
{
WbotDataInstance = WbotData.LoadData();
}
public Task<List<WbotCountry>> GetCountriesAsync()
{
return Task.FromResult(WbotDataInstance.countries);
}
public override Task Delete(WbotCountry c)
{
WbotDataInstance.countries.Add(c);
WbotDataInstance.SaveData();
return Task.CompletedTask;
}
public override Task Add(WbotCountry c)
{
WbotDataInstance.countries.Remove(c);
WbotDataInstance.SaveData();
return Task.CompletedTask;
}
}
}
Any help would be appriciated!