Skip to content

Commit

Permalink
#950 [HxGrid] Allowing MultiSelectionEnabled with InfiniteScroll navi…
Browse files Browse the repository at this point in the history
…gation (virtualized) - basic support (hidden select/deselect all checkbox)
  • Loading branch information
hakenr committed Nov 25, 2024
1 parent 55038e4 commit 978654e
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 27 deletions.
1 change: 1 addition & 0 deletions Havit.Blazor.Components.Web.Bootstrap/Grids/HxGrid.razor
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
SelectedDataItems="@SelectedDataItems"
OnSelectDataItemClicked="HandleMultiSelectSelectDataItemClicked"
OnUnselectDataItemClicked="HandleMultiSelectUnselectDataItemClicked"
SelectDeselectAllHeaderVisible="ContentNavigationModeEffective != GridContentNavigationMode.InfiniteScroll "
OnSelectAllClicked="HandleMultiSelectSelectAllClicked"
OnSelectNoneClicked="HandleMultiSelectSelectNoneClicked" />
}
Expand Down
8 changes: 4 additions & 4 deletions Havit.Blazor.Components.Web.Bootstrap/Grids/HxGrid.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,10 @@ protected override async Task OnParametersSetAsync()

Contract.Requires<InvalidOperationException>(DataProvider != null, $"Property {nameof(DataProvider)} on {GetType()} must have a value.");
Contract.Requires<InvalidOperationException>(CurrentUserState != null, $"Property {nameof(CurrentUserState)} on {GetType()} must have a value.");
Contract.Requires<InvalidOperationException>(!MultiSelectionEnabled || (ContentNavigationModeEffective != GridContentNavigationMode.InfiniteScroll), $"Cannot use multi selection with infinite scroll on {GetType()}.");
if ((ContentNavigationModeEffective == GridContentNavigationMode.InfiniteScroll) && MultiSelectionEnabled)
{
Contract.Requires<InvalidOperationException>(PreserveSelectionEffective, $"{nameof(PreserveSelection)} must be enabled on {nameof(HxGrid)} when using {nameof(GridContentNavigationMode.InfiniteScroll)} with {nameof(MultiSelectionEnabled)}.");
}

if (_previousUserState != CurrentUserState)
{
Expand Down Expand Up @@ -931,7 +934,6 @@ private async Task HandleMultiSelectSelectDataItemClicked(TItem selectedDataItem
private async Task HandleMultiSelectUnselectDataItemClicked(TItem selectedDataItem)
{
Contract.Requires(MultiSelectionEnabled);
Contract.Requires((ContentNavigationModeEffective == GridContentNavigationMode.Pagination) || (ContentNavigationModeEffective == GridContentNavigationMode.LoadMore) || (ContentNavigationModeEffective == GridContentNavigationMode.PaginationAndLoadMore));

var selectedDataItems = SelectedDataItems?.ToHashSet() ?? [];
if (selectedDataItems.Remove(selectedDataItem))
Expand All @@ -943,7 +945,6 @@ private async Task HandleMultiSelectUnselectDataItemClicked(TItem selectedDataIt
private async Task HandleMultiSelectSelectAllClicked()
{
Contract.Requires(MultiSelectionEnabled, nameof(MultiSelectionEnabled));
Contract.Requires((ContentNavigationModeEffective == GridContentNavigationMode.Pagination) || (ContentNavigationModeEffective == GridContentNavigationMode.LoadMore) || (ContentNavigationModeEffective == GridContentNavigationMode.PaginationAndLoadMore));

if (_paginationDataItemsToRender is null)
{
Expand Down Expand Up @@ -971,7 +972,6 @@ private async Task HandleMultiSelectSelectAllClicked()
private async Task HandleMultiSelectSelectNoneClicked()
{
Contract.Requires(MultiSelectionEnabled);
Contract.Requires((ContentNavigationModeEffective == GridContentNavigationMode.Pagination) || (ContentNavigationModeEffective == GridContentNavigationMode.LoadMore) || (ContentNavigationModeEffective == GridContentNavigationMode.PaginationAndLoadMore));

if (PreserveSelectionEffective)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

public class HxMultiSelectGridColumnInternal<TItem> : HxGridColumnBase<TItem>
{
[Parameter, EditorRequired] public bool SelectDeselectAllHeaderVisible { get; set; }
[Parameter] public HashSet<TItem> SelectedDataItems { get; set; }
[Parameter] public bool AllDataItemsSelected { get; set; }
[Parameter] public EventCallback OnSelectAllClicked { get; set; }
Expand All @@ -18,28 +19,35 @@ public class HxMultiSelectGridColumnInternal<TItem> : HxGridColumnBase<TItem>
/// <inheritdoc />
protected override GridCellTemplate GetHeaderCellTemplate(GridHeaderCellContext context)
{
return new GridCellTemplate
if (SelectDeselectAllHeaderVisible)
{
CssClass = "text-center",
Template = (RenderTreeBuilder builder) =>
return new GridCellTemplate
{
builder.OpenElement(100, "input");
builder.AddAttribute(101, "type", "checkbox");
builder.AddAttribute(102, "class", "form-check-input");
CssClass = "text-center",
Template = (RenderTreeBuilder builder) =>
{
builder.OpenElement(100, "input");
builder.AddAttribute(101, "type", "checkbox");
builder.AddAttribute(102, "class", "form-check-input");

builder.AddAttribute(103, "checked", AllDataItemsSelected);
builder.AddAttribute(104, "onchange", EventCallback.Factory.Create<ChangeEventArgs>(this, HandleSelectAllOrNoneClick));
builder.SetUpdatesAttributeName("checked");
builder.AddEventStopPropagationAttribute(105, "onclick", true);
builder.AddAttribute(103, "checked", AllDataItemsSelected);
builder.AddAttribute(104, "onchange", EventCallback.Factory.Create<ChangeEventArgs>(this, HandleSelectAllOrNoneClick));
builder.SetUpdatesAttributeName("checked");
builder.AddEventStopPropagationAttribute(105, "onclick", true);

if ((context.TotalCount is null) || (context.TotalCount == 0))
{
builder.AddAttribute(102, "disabled");
}
if ((context.TotalCount is null) || (context.TotalCount == 0))
{
builder.AddAttribute(102, "disabled");
}

builder.CloseElement(); // input
}
};
builder.CloseElement(); // input
}
};
}
else
{
return GridCellTemplate.Empty;
}
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
<HxGridColumn HeaderText="Location" ItemTextSelector="employee => employee.Location" />
</Columns>
</HxGrid>

<HxSwitch @bind-Value="preserveSelection" Text="@(preserveSelection ? "PreserveSelection=\"true\"" : "PreserveSelection=\"false\"")" />
<hr />
<p>
Selected employees: @(String.Join(", ", selectedEmployees.Select(e => e.Name)))
</p>

<HxSwitch @bind-Value="preserveSelection" Text="@(preserveSelection ? "PreserveSelection=\"true\"" : "PreserveSelection=\"false\"")" />

@code {
private HashSet<EmployeeDto> selectedEmployees = new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,21 +122,22 @@
Enable multi-row selection for users by setting <code>@nameof(HxGrid<object>.MultiSelectionEnabled)="true"</code>.
The selected items can be accessed via the <code>@nameof(HxGrid<object>.SelectedDataItems)</code> parameter, which is bindable.
</p>
<Demo Type="typeof(HxGrid_Demo_Multiselect)" />
<p>
Note that <code>@nameof(HxGrid<TItem>.SelectedDataItems)</code> only includes visible items.
By default, items are removed from the selection when they become unrendered (for example, after paging, sorting, etc.).
However, this behavior can be modified by setting the <code>@nameof(HxGrid<TItem>.PreserveSelection)="true"</code> parameter,
which ensures that selected items are preserved across data operations such as paging, sorting or manual invocation of <code>RefreshDataAsync</code>.
which ensures that selected items are preserved across data operations such as paging, sorting, or manual invocation of <code>RefreshDataAsync</code>.
</p>
<p>
The "select/deselect all" checkbox operates only on visible records and adds/removes them from the selection accordingly.
Non-visible items (e.g., from other pages) are not affected by this operation.
</p>
<DocAlert>
Multi-row selection is not compatible with <code>@nameof(GridContentNavigationMode.InfiniteScroll)</code>.<br />
This design decision might change in the future.
When using <code>@nameof(GridContentNavigationMode.InfiniteScroll)</code>, <code>@nameof(HxGrid<TItem>.PreserveSelection)="true"</code> is required for multi-row selection to work.
Attempting to use <code>@nameof(HxGrid<TItem>.MultiSelectionEnabled)="true"</code> without enabling <code>PreserveSelection</code> will result in an exception. Additionally, the "select/deselect all" checkbox is intentionally hidden in this mode,
as the grid does not have access to all data to reliably perform this operation. For more details, see ticket <a href="https://github.com/havit/Havit.Blazor/issues/950">#950</a>.
</DocAlert>
<Demo Type="typeof(HxGrid_Demo_Multiselect)" />


<DocHeading Title="Inline-editing" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
@page "/HxGrid_InfiniteScroll_MultiSelectionEnabled"
@rendermode InteractiveServer
@inject IDemoDataService DemoDataService

<style>
.virtualized-table-container {
height: 400px;
overflow: auto;
}
</style>

<HxGrid TItem="EmployeeDto"
DataProvider="GetGridData"
MultiSelectionEnabled="true"
PreserveSelection="true"
@bind-SelectedDataItems="selectedEmployees"
TableContainerCssClass="virtualized-table-container"
ContentNavigationMode="GridContentNavigationMode.InfiniteScroll">
<Columns>
<HxGridColumn HeaderText="Name" ItemTextSelector="employee => employee.Name" />
<HxGridColumn HeaderText="Phone" ItemTextSelector="employee => employee.Phone" />
<HxGridColumn HeaderText="Salary" ItemTextSelector="@(employee => employee.Salary.ToString("c0"))" />
<HxGridColumn HeaderText="Position" ItemTextSelector="employee => employee.Position" />
<HxGridColumn HeaderText="Location" ItemTextSelector="employee => employee.Location" />
</Columns>
</HxGrid>

<p>
Selected employees: @(String.Join(", ", selectedEmployees.Select(e => e.Name)))
</p>


@code {
private HashSet<EmployeeDto> selectedEmployees = new();

private async Task<GridDataProviderResult<EmployeeDto>> GetGridData(GridDataProviderRequest<EmployeeDto> request)
{
var response = await DemoDataService.GetEmployeesDataFragmentAsync(request.StartIndex, request.Count, request.CancellationToken);
return new GridDataProviderResult<EmployeeDto>()
{
Data = response.Data,
TotalCount = response.TotalCount
};
}
}

0 comments on commit 978654e

Please sign in to comment.