Skip to content

Commit

Permalink
Grid: Select all or Unselect all items programmatically (#988)
Browse files Browse the repository at this point in the history
* Grid: Select all or Unselect all items programmatically
  • Loading branch information
gvreddy04 authored Dec 25, 2024
1 parent 057cd69 commit a0c46e6
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<div class="mb-3">
<Button Type="ButtonType.Button" Color="ButtonColor.Primary" Size="ButtonSize.Small" @onclick="SelectAllEmployees">Select All</Button>
<Button Type="ButtonType.Button" Color="ButtonColor.Primary" Size="ButtonSize.Small" @onclick="UnselectAllEmployees">Unselect All</Button>
</div>

<Grid @ref="gridRef" TItem="Employee1"
Class="table table-hover table-bordered"
DataProvider="EmployeesDataProvider"
AllowFiltering="true"
AllowSelection="true"
SelectionMode="GridSelectionMode.Multiple"
SelectedItemsChanged="OnSelectedItemsChanged"
Responsive="true">

<GridColumns>
<GridColumn TItem="Employee1" HeaderText="Id" PropertyName="Id">
@context.Id
</GridColumn>
<GridColumn TItem="Employee1" HeaderText="Employee Name" PropertyName="Name">
@context.Name
</GridColumn>
<GridColumn TItem="Employee1" HeaderText="Designation" PropertyName="Designation">
@context.Designation
</GridColumn>
<GridColumn TItem="Employee1" HeaderText="DOJ" PropertyName="DOJ">
@context.DOJ
</GridColumn>
<GridColumn TItem="Employee1" HeaderText="Active" PropertyName="IsActive">
@context.IsActive
</GridColumn>
</GridColumns>

</Grid>

<div class="mt-3">
Selected Items Count: @selectedEmployees.Count
</div>

<div class="mt-2">
Selected Employees:
<ul>
@foreach (var emp in selectedEmployees)
{
<li>@emp.Name</li>
}
</ul>
</div>

@code {
private Grid<Employee1> gridRef;
private IEnumerable<Employee1> employees = default!;

private HashSet<Employee1> selectedEmployees = new();

private async Task<GridDataProviderResult<Employee1>> EmployeesDataProvider(GridDataProviderRequest<Employee1> request)
{
Console.WriteLine("EmployeesDataProvider called...");

if (employees is null) // pull employees only one time for client-side filtering, sorting, and paging
employees = GetEmployees(); // call a service or an API to pull the employees
return await Task.FromResult(request.ApplyTo(employees));
}

private IEnumerable<Employee1> GetEmployees()
{
return new List<Employee1>
{
new Employee1 { Id = 107, Name = "Alice", Designation = "AI Engineer", DOJ = new DateOnly(1998, 11, 17), IsActive = true },
new Employee1 { Id = 103, Name = "Bob", Designation = "Senior DevOps Engineer", DOJ = new DateOnly(1985, 1, 5), IsActive = true },
new Employee1 { Id = 106, Name = "John", Designation = "Data Engineer", DOJ = new DateOnly(1995, 4, 17), IsActive = true },
new Employee1 { Id = 104, Name = "Pop", Designation = "Associate Architect", DOJ = new DateOnly(1985, 6, 8), IsActive = false },
new Employee1 { Id = 105, Name = "Ronald", Designation = "Senior Data Engineer", DOJ = new DateOnly(1991, 8, 23), IsActive = true },
new Employee1 { Id = 102, Name = "Line", Designation = "Architect", DOJ = new DateOnly(1977, 1, 12), IsActive = true },
new Employee1 { Id = 101, Name = "Daniel", Designation = "Architect", DOJ = new DateOnly(1977, 1, 12), IsActive = true },
new Employee1 { Id = 108, Name = "Zayne", Designation = "Data Analyst", DOJ = new DateOnly(1991, 1, 1), IsActive = true },
new Employee1 { Id = 109, Name = "Isha", Designation = "App Maker", DOJ = new DateOnly(1996, 7, 1), IsActive = true },
};
}

private Task OnSelectedItemsChanged(HashSet<Employee1> employees)
{
selectedEmployees = employees is not null && employees.Any() ? employees : new();
return Task.CompletedTask;
}

private Task SelectAllEmployees() => gridRef.SelectAllItemsAsync();

private Task UnselectAllEmployees() => gridRef.UnSelectAllItemsAsync();
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,21 @@
<Demo Type="typeof(Grid_Demo_01_Selection)" Tabs="true" />
</Section>

<Section Size="HeadingSize.H2" Name="Multiple Selection" PageUrl="@pageUrl" Link="multiple-selection">
<Section Size="HeadingSize.H2" Name="Multiple selection" PageUrl="@pageUrl" Link="multiple-selection">
<div class="mb-3">
To select multiple rows, set <code>SelectionMode="GridSelectionMode.Multiple"</code>.
</div>
<Demo Type="typeof(Grid_Demo_02_Multiple_Selection)" Tabs="true" />
<Demo Type="typeof(Grid_Demo_02_A_Multiple_Selection)" Tabs="true" />
<Callout Color="CalloutColor.Danger" Heading="Note">
<p>Selected items are removed from the selection if they are not rendered after paging, sorting, filtering, etc.</p>
</Callout>
</Section>

<Section Size="HeadingSize.H2" Name="Select all or unselect all programmatically" PageUrl="@pageUrl" Link="select-all-or-unselect-all-programmatically">
<div class="mb-3">
<b>Select</b> or <b>unselect</b> all the items programatically by calling the <code>SelectAllItemsAsync()</code> and <code>UnSelectAllItemsAsync()</code> methods. Also, set <code>SelectionMode="GridSelectionMode.Multiple"</code>.
</div>
<Demo Type="typeof(Grid_Demo_02_B_Multiple_Selection_Programmatically)" Tabs="true" />
<Callout Color="CalloutColor.Danger" Heading="Note">
<p>Selected items are removed from the selection if they are not rendered after paging, sorting, filtering, etc.</p>
</Callout>
Expand Down
88 changes: 57 additions & 31 deletions blazorbootstrap/Components/Grid/Grid.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ public partial class Grid<TItem> : BlazorBootstrapComponentBase

private List<GridColumn<TItem>> columns = new();

private GridDetailView<TItem>? detailView;

public GridEmptyDataTemplate<TItem>? emptyDataTemplate;

/// <summary>
/// Current grid state (filters, paging, sorting).
/// </summary>
Expand All @@ -21,18 +25,14 @@ public partial class Grid<TItem> : BlazorBootstrapComponentBase

private RenderFragment? headerSelectionTemplate;

private GridDetailView<TItem>? detailView;

public GridEmptyDataTemplate<TItem>? emptyDataTemplate;

public GridLoadingTemplate<TItem>? loadingTemplate;

private bool isFirstRenderComplete = false;

private List<TItem>? items = null;

private object? lastAssignedDataOrDataProvider;

public GridLoadingTemplate<TItem>? loadingTemplate;

private int pageSize;

private bool requestInProgress = false;
Expand Down Expand Up @@ -69,7 +69,8 @@ protected override void OnInitialized()

protected override Task OnParametersSetAsync()
{
if ((Data is null && DataProvider is null) || (Data is not null && DataProvider is not null)) throw new ArgumentException($"Grid requires either {nameof(Data)} or {nameof(DataProvider)}, but not both or neither.");
if ((Data is null && DataProvider is null) || (Data is not null && DataProvider is not null))
throw new ArgumentException($"Grid requires either {nameof(Data)} or {nameof(DataProvider)}, but not both or neither.");

if (AllowPaging && PageSize < 0)
throw new ArgumentException($"{nameof(PageSize)} must be greater than zero.");
Expand Down Expand Up @@ -123,6 +124,10 @@ protected override Task OnParametersSetAsync()
/// </summary>
public async ValueTask ResetPageNumber() => await ResetPageNumberAsync(true);

public Task SelectAllItemsAsync() => SelectAllItemsInternalAsync(true);

public Task UnSelectAllItemsAsync() => SelectAllItemsInternalAsync(false);

internal void AddColumn(GridColumn<TItem> column) => columns.Add(column);

internal async Task FilterChangedAsync()
Expand Down Expand Up @@ -158,13 +163,13 @@ internal async Task RefreshDataAsync(bool firstRender = false, CancellationToken
await LoadGridSettingsAsync();

var request = new GridDataProviderRequest<TItem>
{
PageNumber = AllowPaging ? gridCurrentState.PageIndex : 0,
PageSize = AllowPaging ? pageSize : 0,
Sorting = AllowSorting ? gridCurrentState.Sorting ?? GetDefaultSorting()! : null!,
Filters = AllowFiltering ? GetFilters()! : null!,
CancellationToken = cancellationToken
};
{
PageNumber = AllowPaging ? gridCurrentState.PageIndex : 0,
PageSize = AllowPaging ? pageSize : 0,
Sorting = AllowSorting ? gridCurrentState.Sorting ?? GetDefaultSorting()! : null!,
Filters = AllowFiltering ? GetFilters()! : null!,
CancellationToken = cancellationToken
};

GridDataProviderResult<TItem> result = default!;

Expand Down Expand Up @@ -205,6 +210,12 @@ internal async ValueTask ResetPageNumberAsync(bool refreshGrid = false)
await RefreshDataAsync(false);
}

internal void SetGridDetailView(GridDetailView<TItem> detailView) => this.detailView = detailView;

internal void SetGridEmptyDataTemplate(GridEmptyDataTemplate<TItem> emptyDataTemplate) => this.emptyDataTemplate = emptyDataTemplate;

internal void SetGridLoadingTemplate(GridLoadingTemplate<TItem> loadingTemplate) => this.loadingTemplate = loadingTemplate;

internal async Task SortingChangedAsync(GridColumn<TItem> column)
{
if (columns == null || !columns.Any())
Expand Down Expand Up @@ -380,13 +391,8 @@ private async Task LoadGridSettingsAsync()

private async Task OnHeaderCheckboxChanged(ChangeEventArgs args)
{
allItemsSelected = bool.TryParse(args?.Value?.ToString(), out var checkboxState) && checkboxState;
selectedItems = allItemsSelected ? new HashSet<TItem>(items!) : new HashSet<TItem>();
SelectedItemsCount = selectedItems.Count;
await CheckOrUnCheckAll();

if (SelectedItemsChanged.HasDelegate)
await SelectedItemsChanged.InvokeAsync(selectedItems);
var headerCheckboxState = bool.TryParse(args?.Value?.ToString(), out var checkboxState) && checkboxState;
await SelectAllItemsInternalAsync(headerCheckboxState);
}

private async Task OnPageChangedAsync(int newPageNumber)
Expand Down Expand Up @@ -495,13 +501,27 @@ private Task SaveGridSettingsAsync()
return GridSettingsChanged.InvokeAsync(settings);
}

private async Task SetCheckboxStateAsync(string id, CheckboxState checkboxState) => await JSRuntime.InvokeVoidAsync("window.blazorBootstrap.grid.setSelectAllCheckboxState", id, (int)checkboxState);
private async Task SelectAllItemsInternalAsync(bool selectAll)
{
if(SelectionMode != GridSelectionMode.Multiple)
return;

internal void SetGridDetailView(GridDetailView<TItem> detailView) => this.detailView = detailView;
allItemsSelected = selectAll;
selectedItems = allItemsSelected ? new HashSet<TItem>(items!) : new HashSet<TItem>();
SelectedItemsCount = allItemsSelected ? selectedItems.Count : 0;

internal void SetGridEmptyDataTemplate(GridEmptyDataTemplate<TItem> emptyDataTemplate) => this.emptyDataTemplate = emptyDataTemplate;
if (allItemsSelected)
await SetCheckboxStateAsync(headerCheckboxId, CheckboxState.Checked);
else
await SetCheckboxStateAsync(headerCheckboxId, CheckboxState.Unchecked);

internal void SetGridLoadingTemplate(GridLoadingTemplate<TItem> loadingTemplate) => this.loadingTemplate = loadingTemplate;
await CheckOrUnCheckAll();

if (SelectedItemsChanged.HasDelegate)
await SelectedItemsChanged.InvokeAsync(selectedItems);
}

private async Task SetCheckboxStateAsync(string id, CheckboxState checkboxState) => await JSRuntime.InvokeVoidAsync("window.blazorBootstrap.grid.setSelectAllCheckboxState", id, (int)checkboxState);

/// <summary>
/// Set filters.
Expand Down Expand Up @@ -534,9 +554,11 @@ private void SetFilters(IEnumerable<FilterItem> filterItems)
#region Properties, Indexers

protected override string? ClassNames =>
BuildClassNames(Class,
BuildClassNames(
Class,
("bb-table", true),
(BootstrapClass.TableSticky, FixedHeader));
(BootstrapClass.TableSticky, FixedHeader)
);

/// <summary>
/// Gets or sets the grid delete.
Expand Down Expand Up @@ -705,8 +727,10 @@ private void SetFilters(IEnumerable<FilterItem> filterItems)
public string? GridContainerClass { get; set; }

private string? GridContainerClassNames =>
BuildClassNames(GridContainerClass,
(BootstrapClass.TableResponsive, Responsive));
BuildClassNames(
GridContainerClass,
(BootstrapClass.TableResponsive, Responsive)
);

/// <summary>
/// Gets or sets the grid container css style.
Expand All @@ -715,8 +739,10 @@ private void SetFilters(IEnumerable<FilterItem> filterItems)
public string? GridContainerStyle { get; set; }

private string? GridContainerStyleNames =>
BuildStyleNames(GridContainerStyle,
($"height:{Height.ToString(CultureInfo.InvariantCulture)}{Unit.ToCssString()}", FixedHeader));
BuildStyleNames(
GridContainerStyle,
($"height:{Height.ToString(CultureInfo.InvariantCulture)}{Unit.ToCssString()}", FixedHeader)
);

/// <summary>
/// This event is fired when the grid state is changed.
Expand Down

0 comments on commit a0c46e6

Please sign in to comment.