Reputation: 866
Using Blazors Quickgrid. I would like to show a row number for the items. They are 'ApplicationUser' Items. (The id is a guid) It looks like this:
<div class="grid" tabindex="-1">
<QuickGrid Items="FilteredUsers" Virtualize="true" @ref="_gridview" >
<PropertyColumn Property="@(u => u.FirstName)" Sortable="true" />
<PropertyColumn Property="@(u => u.LastName)" Sortable="true" Title="Name">
<ColumnOptions>
<input type="search" autofocus @bind="LastNamefilter" @bind:event="oninput" placeholder="Last Name ..." />
</ColumnOptions>
</PropertyColumn>
.
.
.
more stuff..
The 'FilteredUsers' comes from:
private IQueryable<ApplicationUser> FilteredUsers;
private void UpdateUsers()
{
FilteredUsers = _rgContext.Users.Where(u => u.LastName!.Contains(LastNamefilter)
&& u.Email!.Contains(Emailfilter)
&& u.PhoneNumber!.Contains(Phonefilter)
);
totalcount = FilteredUsers.Count();
}
The _rgcontext is created using a contextFactory created in the OnInit function:
protected override async Task OnInitializedAsync()
{
_rgContext = _contextFactory.CreateDbContext();
await _userService.CheckUpdateCurrentUserInfo();
UpdateUsers();
await base.OnInitializedAsync();
return;
}
... and disposed: (THough not sure if this is relevant... seems to work so far.
public void Dispose()
{
_rgContext?.Dispose();
}
I have the totalcount value from the query. But I would like to add a row number. I can't seem to find anywhere I can (say) get a callback to create a column with such a number in it.. Any thoughts?
Upvotes: 0
Views: 238
Reputation: 273524
Easiest option is to use an ItemsProvider. I find that works better for large datasets with EF anyway.
An ItemsProvider is a callback with a request parameter. You can use request.StartIndex
to base your numbering upon. You'll probably want to wrap the entities with a ViewModel to store the number.
Basic idea:
GridItemsProvider<TblAddress>? itemsProvider;
Dictionary<TblAddress, int> rownumLookup = new();
int? GetRowNum(TblAddress e) => rownumLookup.TryGetValue(e, out int row) ? row : null;
protected override async Task OnInitializedAsync()
{
itemsProvider = async request =>
{
using var dbContext = CtxFactory.CreateDbContext();
var query = dbContext.TblAddresses.AsNoTracking();
query = ApplyFilters(query);
var result = new GridItemsProviderResult<TblAddress>
{
TotalItemCount = await query.CountAsync(request.CancellationToken),
Items = await request.ApplySorting(query)
.Skip(request.StartIndex)
.Take(request.Count ?? 100)
.ToListAsync(request.CancellationToken),
};
rownumLookup = result.Items
.Select((TblAddress e, int n) => ( e, n ))
.ToDictionary(t => t.e, t => request.StartIndex + t.n);
return result;
};
}
Upvotes: 1
Reputation: 30177
Here's a quick and dirty demo using the WeatherForecast page. It demonstrates how to use request and result objects to pass data up and down the data pipeline.
First a data request object to pass down the data pipeline to get the data.
public readonly record struct DataRequest(int StartIndex, int PageSize, string SummaryFilter);
My quick and dirty mock up for the data pipeline:
public class DataBroker
{
private List<WeatherForecast> _items = new List<WeatherForecast>();
public event EventHandler<int>? ListUpdated;
public DataBroker()
{
var startDate = DateOnly.FromDateTime(DateTime.Now);
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
_items = Enumerable.Range(1, 5000).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToList();
}
public async ValueTask<GridItemsProviderResult<WeatherForecast>> GetDataAsync(DataRequest request)
{
// simulate an async call
await Task.Delay(500);
var query = _items.Where(item => item.Summary.Contains(request.SummaryFilter));
var count = query.Count();
var list = query
.Skip(request.StartIndex)
.Take(request.PageSize)
.ToList();
this.ListUpdated?.Invoke(null, count);
return new GridItemsProviderResult<WeatherForecast>() { Items= list, TotalItemCount=count };
}
}
Register the data broker in DI Services.
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddScoped<DataBroker>();
var app = builder.Build();
Next, a RecordCount Component to display the count [and update when it changes].
@implements IDisposable
@inject DataBroker _dataBroker
<div class="m-2 alert alert-info">
Count: @_count
</div>
@code {
private int _count;
protected override void OnInitialized()
{
_dataBroker.ListUpdated += this.OnListUpdated;
}
private void OnListUpdated(object? sender, int count)
{
_count = count;
this.StateHasChanged();
}
public void Dispose()
{
_dataBroker.ListUpdated -= this.OnListUpdated;
}
}
And finally the demo Weather Page:
@page "/weather"
@inject DataBroker _dataBroker
<PageTitle>Weather</PageTitle>
<h1>Weather</h1>
<RecordCount />
<QuickGrid TGridItem="WeatherForecast" ItemsProvider="this.GetDataAsync" Virtualize>
<PropertyColumn Title="Date" Property="@(c => c!.Date)" Format="dd-MMM-yy" />
<PropertyColumn Title="Temp ° C" Sortable="true" Format="N0" Property="@(c => c!.TemperatureC)" Align=Align.End />
<PropertyColumn Title="Temp ° F" Sortable="true" Format="N0" Property="@(c => c!.TemperatureF)" Align=Align.End />
<PropertyColumn Title="Summary" Sortable="true" Property="@(c => c!.Summary)" Align=Align.Center />
</QuickGrid>
@code {
private ValueTask<GridItemsProviderResult<WeatherForecast>> GetDataAsync(GridItemsProviderRequest<WeatherForecast> request)
{
// Build a data request from the GridItemsProviderRequest provided by Quickgrid
var dataRequest = new DataRequest(request.StartIndex, request.Count ?? 0, "Hot");
return _dataBroker.GetDataAsync(dataRequest);
}
}
Upvotes: 1