Blazor QuickGrid Dynamic Columns
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 281
Description
### Is there an existing issue for this?
- [X] I have searched the existing issues
### Is your feature request related to a problem? Please describe the problem.
I want do add my columns dynamically, as they are used in multiple places across the project, including these columns are optional.
The current **QuickGrid** implementation is pretty much solid for lots of scenarios, but usually whoever is using any grids, they might be using many of them.
Currently suggested solution (https://aspnet.github.io/quickgridsamples/columns)
```razor
@if (showName)
{
@context.LastName, @context.FirstName
}
@if (showBirthDate)
{
}
```
### Describe the solution you'd like
Some options are described below but I believe someone can come up even with better solutions.
**Option 1** - Expose `Columns` property, which could be used to (re)assign columns
```razor
```
**Option 2** - I am messing/prototyping around currently, is allowing a component inside QuickGrid, which would wrap my different types of columns implementation.
The example below defines an empty column; and QuickGridColumns component defines additional columns, which are generated dynamically through custom columns and existing QuickGrid colums.
```razor
@* This is not working *@
@* I have to use this instead everywhere 😭😭😭😭😭😭😭😭😭 *@
@foreach (var col in _columnManager.Get())
{
@if (col.ColumnType == typeof(TickColumn))
{
}
else if (col.ColumnType == typeof(ImageColumn))
{
}
else
{
}
}
```
**QuickGridColumns.razor**
```razor
@* This solution is not working for some reason, inside the QuickGrid, it doesn't render currently so I have to repeat the foreach loop in every grid 😭🤦♂️ *@
@typeparam TGridItem
@if (ColumnManager is not null)
{
@foreach (var col in ColumnManager.Get())
{
@if (col.ColumnType == typeof(TickColumn))
{
}
else if (col.ColumnType == typeof(ImageColumn))
{
}
else
{
}
}
}
@code {
[Parameter] public ColumnManager? ColumnManager { get; set; }
protected override void OnParametersSet()
{
StateHasChanged();
}
}
```
**ColumnManager**
Here you can see how easily I can create new columns, including adding predefined, strongly typed columns e.g `AddConsultantName()`
```csharp
public class ColumnManager
{
public readonly List> Columns = new();
///
/// Returns visible columns
///
///
public IEnumerable> Get() => Columns.Where(w => w.Visible);
public void Add(ColumnTemplate? column = default)
{
if (column == null) return;
if (string.IsNullOrWhiteSpace(column.Title))
{
column.Title = GetPropertyName(column.Property) ?? "Title n/a";
}
Columns.Add(column);
column.Id = Columns.Count;
}
public void AddSimple(Expression> expression)
{
Add(new() { Property = expression });
}
public void AddTickColumn(Expression> expression, string? title = null, Align align = Align.Center)
{
Add(new() { Property = expression, ColumnType = typeof(TickColumn), Title = title, Align = align });
}
public void AddConsultantName() => Add(new ColumnTemplate
{
Title = "Consultant Name",
FullTitle = "Consultant Name",
Property = s => s == null ? string.Empty : ((ICorporateUserDto)s).ConsultantName
});
public void AddConsultantId() => Add(new ColumnTemplate
{
Title = "Consult.Id",
FullTitle = "Consultant Id",
Property = s => s == null ? string.Empty : ((ICorporateUserDto)s).ConsultantId
});
public void AddDateAdded() => Add(new ColumnTemplate
{
Title = "Date Added",
FullTitle = "Date Added",
Property = s => s == null ? default : ((IDateAdded)s).DateAdded
});
public void AddCreatedOn(string? format = null) => Add(new ColumnTemplate
{
Title = "Date Added",
FullTitle = "Date Added",
//Format = "dd/MM/yyyy",
Property = s => s == null ? default : ((ICreatedOn)s).CreatedOn
});
private static string? GetPropertyName(Expression>? expression)
{
if (expression is null) return null;
MemberExpression? memberExpression;
if (expression.Body is UnaryExpression unaryExpression)
{
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = expression.Body as MemberExpression;
}
if (memberExpression == null)
{
throw new ArgumentException($"Expression '{expression}' refers to a method, not a property.");
}
if (!(memberExpression.Member is PropertyInfo propertyInfo))
{
throw new ArgumentException($"Expression '{expression}' refers to a field, not a property.");
}
return propertyInfo.Name;
}
}
```
**ColumnTemplate**
I wish I could use/inherit from existing `PropertyColumn` but currently this QuickGrid `PropertyColumn` should be used only from editor?
```csharp
public class ColumnTemplate
{
// We need id so we could list all columns e.g. as checkbox and select which one is visible
public int Id { get; set; }
public string ColumnId => $"column-{Id}";
public bool Visible { get; set; } = true;
public bool Sortable { get; set; } = true;
public string? Title { get; set; } = string.Empty;
public string? FullTitle
{
get => string.IsNullOrWhiteSpace(_fullTitle) ? Title : _fullTitle;
set => _fullTitle = value;
}
public Align Align { get; set; }
public string? Format { get; set; }
public Expression>? Property { get; set; }
public Type ColumnType { get; set; } = typeof(PropertyColumn);
private string? _fullTitle;
}
```
**Final result**
You can probably see how easy it is to add columns, have availability to choose dynamically which columns should be added/updated, including an option to create own logic to filter and sort columns.
```razor
@code {
private ColumnManager _columnManager = new();
protected override async Task OnInitializedAsync()
{
_columnManager.Add(new() { Property = p => p.Id, Title = "User Id", Align = Align.Center });
_columnManager.AddSimple(p => p.Name);
_columnManager.AddTickColumn(p => p.IsEnabled, "Enabled");
_columnManager.AddDateAdded();
_columnManager.AddConsultantName();
}
}
```
### Additional context
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.