dotnet / dotnet/aspnetcore

Blazor QuickGrid Dynamic Columns

Open
#47,218 7 comments 5 reactions 0 assignees View on GitHub
area-blazor enhancement feature-blazor-quickgrid
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.