Skip to content

Commit

Permalink
Grid: Filters - Enum support #485
Browse files Browse the repository at this point in the history
  • Loading branch information
gvreddy04 committed May 3, 2024
1 parent 26057aa commit 8542598
Show file tree
Hide file tree
Showing 12 changed files with 271 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@
<SectionHeading Size="HeadingSize.H2" Text="Auto hide paging" PageUrl="@pageUrl" HashTagName="auto-hide-paging" />
<Demo Type="typeof(Grid_Demo_34_AutoHide_Paging)" Tabs="true" />

<SectionHeading Size="HeadingSize.H2" Text="Filter with enum" PageUrl="@pageUrl" HashTagName="filter-with-enum" />
<Demo Type="typeof(Grid_Demo_35_Enum_Filters)" Tabs="true" />

@code {
private string pageUrl = "/grid";
private string title = "Blazor Grid Component";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<Grid @ref="grid"
TItem="User"
Class="table table-hover table-bordered table-striped"
DataProvider="UsersDataProvider"
AllowFiltering="true"
Responsive="true">

<GridColumn TItem="User" HeaderText="Id" PropertyName="Id">
@context.Id
</GridColumn>
<GridColumn TItem="User" HeaderText="User Name" PropertyName="Name">
@context.Name
</GridColumn>
<GridColumn TItem="User" HeaderText="DOB" PropertyName="DOB">
@context.DOB
</GridColumn>
<GridColumn TItem="User" HeaderText="Status" PropertyName="Status">
@context.Status
</GridColumn>

</Grid>

@code {
BlazorBootstrap.Grid<User> grid = default!;
private IEnumerable<User> users = default!;

protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
}

private async Task<GridDataProviderResult<User>> UsersDataProvider(GridDataProviderRequest<User> request)
{
if (users is null) // pull employees only one time for client-side filtering, sorting, and paging
users = GetUsers(); // call a service or an API to pull the employees
return await Task.FromResult(request.ApplyTo(users));
}

private IEnumerable<User> GetUsers()
{
return new List<User>
{
new User { Id = 107, Name = "Alice", DOB = new DateOnly(1998, 11, 17), Status = UserStatus.Registered },
new User { Id = null, Name = "Bob", DOB = new DateOnly(1985, 1, 5), Status = UserStatus.Verified },
new User { Id = 106, Name = "John", DOB = new DateOnly(1995, 4, 17), Status = UserStatus.Registered },
new User { Id = 104, Name = "Pop", DOB = new DateOnly(1985, 6, 8), Status = UserStatus.Registered },
new User { Id = 105, Name = "Ronald", DOB = new DateOnly(1991, 8, 23), Status = UserStatus.VerificationPending },
new User { Id = 102, Name = "Line", DOB = new DateOnly(1977, 1, 12), Status = UserStatus.VerificationPending },
new User { Id = 101, Name = "Daniel", DOB = new DateOnly(1977, 1, 12), Status = UserStatus.Registered },
new User { Id = 108, Name = "Zayne", DOB = new DateOnly(1991, 1, 1), Status = UserStatus.Verified },
new User { Id = 109, Name = "Isha", DOB = null, Status = UserStatus.Verified },
new User { Id = 110, Name = "Vijay", DOB = new DateOnly(1990, 7, 1), Status = UserStatus.Verified },
};
}

public record class User
{
public int? Id { get; set; }
public string? Name { get; set; }
public DateOnly? DOB { get; set; }
public UserStatus Status { get; set; }
}

public enum UserStatus
{
Registered,
VerificationPending,
Verified
}
}
1 change: 1 addition & 0 deletions blazorbootstrap/Components/Grid/Grid.razor
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
FiltersTranslationProvider="GridFiltersTranslationProviderAsync"
FixedHeader="@FixedHeader"
GridColumnFilterChanged="async args => await column.OnFilterChangedAsync(args, column)"
PropertyType="@column.GetPropertyType()"
PropertyTypeName="@column.GetPropertyTypeName()"
Unit="@Unit" />
}
Expand Down
7 changes: 7 additions & 0 deletions blazorbootstrap/Components/Grid/GridColumn.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ protected override async Task OnInitializedAsync()

internal string GetFilterValue() => filterValue;

internal Type GetPropertyType() => typeof(TItem).GetPropertyType(PropertyName)!;

internal string GetPropertyTypeName() => typeof(TItem).GetPropertyTypeName(PropertyName);

internal IEnumerable<SortingItem<TItem>> GetSorting()
Expand Down Expand Up @@ -97,6 +99,11 @@ or StringConstants.PropertyTypeNameDecimal
if (filterOperator == FilterOperator.None)
FilterOperator = filterOperator = FilterOperator.Equals;
}
else if (propertyTypeName == StringConstants.PropertyTypeNameEnum)
{
if (filterOperator == FilterOperator.None)
FilterOperator = filterOperator = FilterOperator.Equals;
}
}

internal void SetFilterOperator(FilterOperator filterOperator) => FilterOperator = this.filterOperator = filterOperator;
Expand Down
46 changes: 35 additions & 11 deletions blazorbootstrap/Components/Grid/GridColumnFilter.razor
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
@if (string.IsNullOrWhiteSpace(filterValue))
{
<span class="me-2">
<Icon Name="IconName.Funnel"/>
<Icon Name="IconName.Funnel" />
</span>
}
else
{
<span class="me-2">
<Icon Name="IconName.FunnelFill"/>
<Icon Name="IconName.FunnelFill" />
</span>
}
<span>@selectedFilterSymbol</span>
Expand All @@ -32,28 +32,52 @@
</ul>

@if (PropertyTypeName == StringConstants.PropertyTypeNameInt16
|| PropertyTypeName == StringConstants.PropertyTypeNameInt32
|| PropertyTypeName == StringConstants.PropertyTypeNameInt64
|| PropertyTypeName == StringConstants.PropertyTypeNameSingle // float
|| PropertyTypeName == StringConstants.PropertyTypeNameDecimal
|| PropertyTypeName == StringConstants.PropertyTypeNameDouble)
|| PropertyTypeName == StringConstants.PropertyTypeNameInt32
|| PropertyTypeName == StringConstants.PropertyTypeNameInt64
|| PropertyTypeName == StringConstants.PropertyTypeNameSingle // float
|| PropertyTypeName == StringConstants.PropertyTypeNameDecimal
|| PropertyTypeName == StringConstants.PropertyTypeNameDouble)
{
<input class="form-control" style="@filterStyle" type="number" value="@filterValue" @oninput="@(async args => await OnFilterValueChangedAsync(args))">
}
else if (PropertyTypeName == StringConstants.PropertyTypeNameDateOnly)
{
<input class="form-control" style="@filterStyle" type="date" value="@filterValue" @oninput="@(async args => await OnFilterValueChangedAsync(args))"/>
<input class="form-control" style="@filterStyle" type="date" value="@filterValue" @oninput="@(async args => await OnFilterValueChangedAsync(args))" />
}
else if (PropertyTypeName == StringConstants.PropertyTypeNameDateTime)
{
<input class="form-control" style="@filterStyle" type="datetime-local" value="@filterValue" @oninput="@(async args => await OnFilterValueChangedAsync(args))"/>
<input class="form-control" style="@filterStyle" type="datetime-local" value="@filterValue" @oninput="@(async args => await OnFilterValueChangedAsync(args))" />
}
else if (PropertyTypeName == StringConstants.PropertyTypeNameBoolean)
{
<input class="form-check-input" type="checkbox" value="@filterValue" @onchange="@(async args => await OnFilterValueChangedAsync(args))"/>
<input class="form-check-input" type="checkbox" value="@filterValue" @onchange="@(async args => await OnFilterValueChangedAsync(args))" />
}
else if (PropertyTypeName == StringConstants.PropertyTypeNameEnum)
{
<Dropdown>
<DropdownToggleButton Class="px-1" Color="ButtonColor.Light">
@if (string.IsNullOrWhiteSpace(filterValue))
{
<span class="px-2">Select</span>
}
else
{
<span class="px-2">@filterValue</span>
}
</DropdownToggleButton>
<DropdownMenu>
@if (PropertyType is not null)
{
@foreach (var item in Enum.GetValues(PropertyType!))
{
<DropdownItem @onclick="@(async () => await OnEnumFilterValueChangedAsync(item))">@item</DropdownItem>
}
}
</DropdownMenu>
</Dropdown>
}
else // string
{
<input class="form-control" style="@filterStyle" type="text" value="@filterValue" @oninput="@(async args => await OnFilterValueChangedAsync(args))"/>
<input class="form-control" style="@filterStyle" type="text" value="@filterValue" @oninput="@(async args => await OnFilterValueChangedAsync(args))" />
}
</div>
20 changes: 20 additions & 0 deletions blazorbootstrap/Components/Grid/GridColumnFilter.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ or StringConstants.PropertyTypeNameDecimal
if (filterOperator is FilterOperator.None or FilterOperator.Clear)
filterOperator = FilterOperator.Equals;
}
else if (PropertyTypeName == StringConstants.PropertyTypeNameEnum)
{
if (filterOperator is FilterOperator.None or FilterOperator.Clear)
filterOperator = FilterOperator.Equals;
}
}

private async Task<IEnumerable<FilterOperatorInfo>> GetFilterOperatorsAsync(string propertyTypeName)
Expand Down Expand Up @@ -97,6 +102,14 @@ private async Task OnFilterOperatorChangedAsync(FilterOperatorInfo filterOperato
await GridColumnFilterChanged.InvokeAsync(new FilterEventArgs(filterValue!, filterOperator));
}

private async Task OnEnumFilterValueChangedAsync(object enumValue)
{
filterValue = enumValue?.ToString();

if (GridColumnFilterChanged.HasDelegate)
await GridColumnFilterChanged.InvokeAsync(new FilterEventArgs(filterValue!, filterOperator));
}

private async Task OnFilterValueChangedAsync(ChangeEventArgs args)
{
filterValue = args?.Value?.ToString();
Expand All @@ -121,6 +134,7 @@ or StringConstants.PropertyTypeNameDecimal
or StringConstants.PropertyTypeNameDateTime)
selectedFilterSymbol = filterOperators?.FirstOrDefault(x => x.FilterOperator == filterOperator)?.Symbol;
else if (PropertyTypeName == StringConstants.PropertyTypeNameBoolean) selectedFilterSymbol = filterOperators?.FirstOrDefault(x => x.FilterOperator == filterOperator)?.Symbol;
else if (PropertyTypeName == StringConstants.PropertyTypeNameEnum) selectedFilterSymbol = filterOperators?.FirstOrDefault(x => x.FilterOperator == filterOperator)?.Symbol;
}

#endregion
Expand Down Expand Up @@ -168,6 +182,12 @@ or StringConstants.PropertyTypeNameDecimal
[Parameter]
public string? PropertyTypeName { get; set; }

/// <summary>
/// Gets or sets the filter property name.
/// </summary>
[Parameter]
public Type? PropertyType { get; set; }

/// <summary>
/// Gets or sets the units.
/// </summary>
Expand Down
59 changes: 59 additions & 0 deletions blazorbootstrap/Extensions/ExpressionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public static Expression<Func<TItem, bool>> And<TItem>(this Expression<Func<TIte
return Expression.Lambda<Func<TItem, bool>>(body, parameterExpression);
}

#region Boolean

public static ConstantExpression GetBooleanConstantExpression(FilterItem filterItem, string propertyTypeName)
{
ConstantExpression? value = null;
Expand Down Expand Up @@ -45,6 +47,10 @@ public static Expression<Func<TItem, bool>> GetBooleanNotEqualExpressionDelegate
return Expression.Lambda<Func<TItem, bool>>(expression, parameterExpression);
}

#endregion Boolean

#region Date

public static ConstantExpression GetDateConstantExpression(FilterItem filterItem, string propertyTypeName)
{
if (filterItem.Value == null)
Expand Down Expand Up @@ -294,8 +300,44 @@ public static Expression<Func<TItem, bool>> GetDateNotEqualExpressionDelegate<TI
return Expression.Lambda<Func<TItem, bool>>(nonNullComparisonExpression, parameterExpression);
}

#endregion Date

#region Enum

public static ConstantExpression GetEnumConstantExpression<TItem>(FilterItem filterItem, Type propertyType, string propertyTypeName)
{
ConstantExpression? value = null;

if(propertyType is not null && propertyType.IsEnum)
{
_ = Enum.TryParse(propertyType, filterItem.Value, out object filterValue);
value = Expression.Constant(filterValue);
}

return value!;
}

public static Expression<Func<TItem, bool>> GetEnumEqualExpressionDelegate<TItem>(ParameterExpression parameterExpression, FilterItem filterItem, Type propertyType, string propertyTypeName)
{
var property = Expression.Property(parameterExpression, filterItem.PropertyName);
var expression = Expression.Equal(property, GetEnumConstantExpression<TItem>(filterItem, propertyType, propertyTypeName));

return Expression.Lambda<Func<TItem, bool>>(expression, parameterExpression);
}

public static Expression<Func<TItem, bool>> GetEnumNotEqualExpressionDelegate<TItem>(ParameterExpression parameterExpression, FilterItem filterItem, Type propertyType, string propertyTypeName)
{
var property = Expression.Property(parameterExpression, filterItem.PropertyName);
var expression = Expression.NotEqual(property, GetEnumConstantExpression<TItem>(filterItem, propertyType, propertyTypeName));

return Expression.Lambda<Func<TItem, bool>>(expression, parameterExpression);
}

#endregion Enum

public static Expression<Func<TItem, bool>>? GetExpressionDelegate<TItem>(ParameterExpression parameterExpression, FilterItem filterItem)
{
var propertyType = typeof(TItem).GetPropertyType(filterItem.PropertyName);
var propertyTypeName = typeof(TItem).GetPropertyTypeName(filterItem.PropertyName);

if (propertyTypeName is StringConstants.PropertyTypeNameInt16
Expand Down Expand Up @@ -348,9 +390,20 @@ or StringConstants.PropertyTypeNameDecimal
_ => GetBooleanEqualExpressionDelegate<TItem>(parameterExpression, filterItem, propertyTypeName)
};

// Enum
if (propertyTypeName == StringConstants.PropertyTypeNameEnum)
return filterItem.Operator switch
{
FilterOperator.Equals => GetEnumEqualExpressionDelegate<TItem>(parameterExpression, filterItem, propertyType, propertyTypeName),
FilterOperator.NotEquals => GetEnumNotEqualExpressionDelegate<TItem>(parameterExpression, filterItem, propertyType, propertyTypeName),
_ => GetEnumEqualExpressionDelegate<TItem>(parameterExpression, filterItem, propertyType, propertyTypeName)
};

return null;
}

#region Number

public static ConstantExpression GetNumberConstantExpression(FilterItem filterItem, string propertyTypeName)
{
if (filterItem.Value is null)
Expand Down Expand Up @@ -560,6 +613,10 @@ public static Expression<Func<TItem, bool>> GetNumberNotEqualExpressionDelegate<
return Expression.Lambda<Func<TItem, bool>>(finalExpression, parameterExpression);
}

#endregion Number

#region String

public static Expression<Func<TItem, bool>> GetStringContainsExpressionDelegate<TItem>(ParameterExpression parameterExpression, FilterItem filterItem)
{
var propertyExp = Expression.Property(parameterExpression, filterItem.PropertyName);
Expand Down Expand Up @@ -656,6 +713,8 @@ public static Expression<Func<TItem, bool>> GetStringStartsWithExpressionDelegat
return Expression.Lambda<Func<TItem, bool>>(finalExpression, parameterExpression);
}

#endregion String

public static bool IsNullableType(this Type type) => Nullable.GetUnderlyingType(type) != null;

public static Expression<Func<TItem, bool>> Or<TItem>(this Expression<Func<TItem, bool>> leftExpression, Expression<Func<TItem, bool>> rightExpression)
Expand Down
36 changes: 35 additions & 1 deletion blazorbootstrap/Extensions/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,24 @@ public static string GetPropertyTypeName(this Type type, string propertyName)
if (type is null || string.IsNullOrWhiteSpace(propertyName))
return string.Empty;

var propertyTypeName = type.GetProperty(propertyName)?.PropertyType?.ToString();
var propertyType = type.GetProperty(propertyName)?.PropertyType;
if (propertyType is null)
return string.Empty;

var propertyTypeName = propertyType?.ToString();

//if(propertyTypeName is null) // Nullable type scenario
//{
// var props = type.GetProperties();
// if(props.Length > 0)
// {
// var propertyInfo = props?.FirstOrDefault(x=>x.Name == propertyName);
// if (propertyInfo is null)
// throw new InvalidDataException($"PropertyName `{propertyName}` is invalid.");

// propertyTypeName = Nullable.GetUnderlyingType(propertyInfo.PropertyType)?.FullName;
// }
//}

if (string.IsNullOrWhiteSpace(propertyTypeName))
return string.Empty;
Expand Down Expand Up @@ -56,8 +73,25 @@ public static string GetPropertyTypeName(this Type type, string propertyName)
if (propertyTypeName.Contains(StringConstants.PropertyTypeNameBoolean, StringComparison.InvariantCulture))
return StringConstants.PropertyTypeNameBoolean;

if (propertyType!.IsEnum)
return StringConstants.PropertyTypeNameEnum;

return string.Empty;
}

/// <summary>
/// Get property type.
/// </summary>
/// <param name="type"></param>
/// <param name="propertyName"></param>
/// <returns>Type?</returns>
public static Type? GetPropertyType(this Type type, string propertyName)
{
if (type is null || string.IsNullOrWhiteSpace(propertyName))
return null;

return type.GetProperty(propertyName)?.PropertyType;
}

#endregion
}
Loading

0 comments on commit 8542598

Please sign in to comment.