I have been trying several times now to use the DataGrid example from the website, and create a generic component so that the functionality in that can be reproduced and contained in the usecase of having several tables for simple data CRUD. I can not get it to work and it's becomming a huge frustration point. Can anyone take a look at this and tell me what am I missing? It's almost working appart from the fact that the render fragment itself is not being rendered for whatever reason.
I am aiming for something like this:
<DataGrid TItem="CategoryDtO"
GetItems="@GetItems"
SendCreateItemCommand="@SendCreateItemCommand"
SendDeleteItemCommand="@SendDeleteItemCommand"
SendUpdateItemCommand="@SendUpdateItemCommand">
<RowTemplate Context="category">
<RadzenDataGridColumn TItem="CategoryDtO" Title="Name" Property="@category.Name">
<EditTemplate>
<RadzenTextBox @bind-Value="@category.Name" />
</EditTemplate>
</RadzenDataGridColumn>
</RowTemplate>
</DataGrid>
@code
{
[Inject] public IMediator Mediator { get; set; }
[Parameter] public Guid BudgetSheetId { get; set; }
public async Task<IEnumerable<CategoryDtO>> GetItems()
{
var cts = new CancellationTokenSource();
var token = cts.Token;
var command = new GetBudgetSheetCategories { BudgetSheetId = BudgetSheetId };
var result = await Mediator.Send(command, token);
return result;
}
public async Task<bool> SendCreateItemCommand(CategoryDtO item)
{
var cts = new CancellationTokenSource();
var token = cts.Token;
var command = new CreateBudgetSheetCategory { BudgetSheetId = BudgetSheetId, CategoryName = item.Name};
var result = await Mediator.Send(command, token);
return result;
}
public async Task<bool> SendDeleteItemCommand(CategoryDtO item)
{
var cts = new CancellationTokenSource();
var token = cts.Token;
var command = new DeleteBudgetSheetCategory { BudgetSheetId = BudgetSheetId, CategoryName = item.Name };
var result = await Mediator.Send(command, token);
return result;
}
public async Task<bool> SendUpdateItemCommand(CategoryDtO item)
{
// var cts = new CancellationTokenSource();
// var token = cts.Token;
// var command = new UpdateBudgetSheetCategory { BudgetSheetId = BudgetSheetId, CategoryName = item.Name };
// var result = await Mediator.Send(command, token);
// return result;
throw new NotImplementedException();
}
}
In the above component, I inherig the DataGrid that I make, then I can pass on a function for each of the basic crud actions, as well as a RenderFragment that applies to the type parameter for rendering each row.
For the DataGrid component I have the following markup:
@typeparam TItem where TItem : new()
<style>
.rz-grid-table {
width: unset;
}
</style>
<RadzenStack Orientation="Orientation.Horizontal" AlignItems="AlignItems.Center" Gap="10px" class="mt-2 mb-4">
<RadzenButton ButtonStyle="ButtonStyle.Success" Icon="add_circle_outline" Text="Add New Order" Click="@InsertRow"/>
</RadzenStack>
<RadzenDataGrid @ref="itemsGrid" AllowAlternatingRows="false" AllowFiltering="true" AllowPaging="true" PageSize="5" AllowSorting="true" EditMode="DataGridEditMode.Single"
Data="@Model.items" TItem="TItem" RowUpdate="@OnUpdateRow" RowCreate="@OnCreateRow" Sort="@Reset" Page="@Reset" Filter="@Reset" ColumnWidth="200px">
<Columns>
@foreach (var item in Model.items)
{
@RowTemplate(item)
}
</Columns>
</RadzenDataGrid>
Here is where I have the code from the data grid example on the webpage, difference being I use the RowTemplate render fragment to populate the columns element.
Then in the codebehind file I have the partial class and the table model:
public class DataGridModel<TItem>
{
internal IEnumerable<TItem> items { get; set; } = new List<TItem>();
public List<TItem> ItemsToInsert { get; set; } = new List<TItem>();
public List<TItem> ItemsToUpdate { get; set; } = new List<TItem>();
}
public partial class DataGrid<TItem> : ComponentBase where TItem : new()
{
[Inject] public IMediator Mediator { get; set; }
[Parameter] public required Func<TItem, Task> SendCreateItemCommand { get; set; }
[Parameter] public required Func<TItem, Task> SendUpdateItemCommand { get; set; }
[Parameter] public required Func<TItem, Task> SendDeleteItemCommand { get; set; }
// Parameter functions for getting the data
[Parameter] public required Func<Task<IEnumerable<TItem>>> GetItems { get; set; }
[Parameter] public RenderFragment<TItem> RowTemplate { get; set; }
protected RadzenDataGrid<TItem> itemsGrid;
protected DataGridModel<TItem?> Model = new DataGridModel<TItem?>();
protected void Reset()
{
Guard.Against.Null(Model, nameof(Model));
Model.ItemsToInsert.Clear();
Model.ItemsToUpdate.Clear();
}
protected void Reset(TItem? item)
{
Guard.Against.Null(Model, nameof(Model));
Model.ItemsToInsert.Remove(item);
Model.ItemsToUpdate.Remove(item);
}
protected override async Task OnInitializedAsync()
{
Guard.Against.Null(Mediator, nameof(Mediator));
await base.OnInitializedAsync();
await LoadData();
}
private async Task LoadData()
{
Model.items = await GetItems();
}
private async Task ReloadGrid()
{
await LoadData();
await itemsGrid.Reload();
}
protected async Task EditRow(TItem? item)
{
if (Model.ItemsToInsert.Count() > 0)
{
Reset();
}
Model.ItemsToUpdate.Add(item);
await itemsGrid.EditRow(item);
}
protected async void OnUpdateRow(TItem item)
{
Reset(item);
await SendUpdateItemCommand(item);
await ReloadGrid();
}
protected async Task SaveRow(TItem item)
{
await itemsGrid.UpdateRow(item);
}
protected void CancelEdit(TItem? item)
{
Reset(item);
itemsGrid.CancelEditRow(item);
}
protected async Task DeleteRow(TItem? item)
{
Reset(item);
if (Model.items.Contains(item))
{
await SendDeleteItemCommand(item);
await ReloadGrid();
}
else
{
itemsGrid.CancelEditRow(item);
await ReloadGrid();
}
}
protected async Task InsertRow()
{
Reset();
var item = new TItem();
Model.ItemsToInsert.Add(item);
await itemsGrid.InsertRow(item);
}
protected async Task OnCreateRow(TItem? item)
{
await SendCreateItemCommand(item);
Model.ItemsToInsert.Remove(item);
}
}
Here I have the same code from the example, but I replace the direct db calls with calls to the separate Func items, that are passed on as parameters when the table is instantiated.
When I try to render the table however, nothing is rendered. Any clues as to why? I had a similar setup working through a regular html table, so I would be curious if there's something Razor specific that prevents this from happening?