Skip to content

Commit a0c46e6

Browse files
authored
Grid: Select all or Unselect all items programmatically (#988)
* Grid: Select all or Unselect all items programmatically
1 parent 057cd69 commit a0c46e6

File tree

4 files changed

+159
-33
lines changed

4 files changed

+159
-33
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<div class="mb-3">
2+
<Button Type="ButtonType.Button" Color="ButtonColor.Primary" Size="ButtonSize.Small" @onclick="SelectAllEmployees">Select All</Button>
3+
<Button Type="ButtonType.Button" Color="ButtonColor.Primary" Size="ButtonSize.Small" @onclick="UnselectAllEmployees">Unselect All</Button>
4+
</div>
5+
6+
<Grid @ref="gridRef" TItem="Employee1"
7+
Class="table table-hover table-bordered"
8+
DataProvider="EmployeesDataProvider"
9+
AllowFiltering="true"
10+
AllowSelection="true"
11+
SelectionMode="GridSelectionMode.Multiple"
12+
SelectedItemsChanged="OnSelectedItemsChanged"
13+
Responsive="true">
14+
15+
<GridColumns>
16+
<GridColumn TItem="Employee1" HeaderText="Id" PropertyName="Id">
17+
@context.Id
18+
</GridColumn>
19+
<GridColumn TItem="Employee1" HeaderText="Employee Name" PropertyName="Name">
20+
@context.Name
21+
</GridColumn>
22+
<GridColumn TItem="Employee1" HeaderText="Designation" PropertyName="Designation">
23+
@context.Designation
24+
</GridColumn>
25+
<GridColumn TItem="Employee1" HeaderText="DOJ" PropertyName="DOJ">
26+
@context.DOJ
27+
</GridColumn>
28+
<GridColumn TItem="Employee1" HeaderText="Active" PropertyName="IsActive">
29+
@context.IsActive
30+
</GridColumn>
31+
</GridColumns>
32+
33+
</Grid>
34+
35+
<div class="mt-3">
36+
Selected Items Count: @selectedEmployees.Count
37+
</div>
38+
39+
<div class="mt-2">
40+
Selected Employees:
41+
<ul>
42+
@foreach (var emp in selectedEmployees)
43+
{
44+
<li>@emp.Name</li>
45+
}
46+
</ul>
47+
</div>
48+
49+
@code {
50+
private Grid<Employee1> gridRef;
51+
private IEnumerable<Employee1> employees = default!;
52+
53+
private HashSet<Employee1> selectedEmployees = new();
54+
55+
private async Task<GridDataProviderResult<Employee1>> EmployeesDataProvider(GridDataProviderRequest<Employee1> request)
56+
{
57+
Console.WriteLine("EmployeesDataProvider called...");
58+
59+
if (employees is null) // pull employees only one time for client-side filtering, sorting, and paging
60+
employees = GetEmployees(); // call a service or an API to pull the employees
61+
62+
return await Task.FromResult(request.ApplyTo(employees));
63+
}
64+
65+
private IEnumerable<Employee1> GetEmployees()
66+
{
67+
return new List<Employee1>
68+
{
69+
new Employee1 { Id = 107, Name = "Alice", Designation = "AI Engineer", DOJ = new DateOnly(1998, 11, 17), IsActive = true },
70+
new Employee1 { Id = 103, Name = "Bob", Designation = "Senior DevOps Engineer", DOJ = new DateOnly(1985, 1, 5), IsActive = true },
71+
new Employee1 { Id = 106, Name = "John", Designation = "Data Engineer", DOJ = new DateOnly(1995, 4, 17), IsActive = true },
72+
new Employee1 { Id = 104, Name = "Pop", Designation = "Associate Architect", DOJ = new DateOnly(1985, 6, 8), IsActive = false },
73+
new Employee1 { Id = 105, Name = "Ronald", Designation = "Senior Data Engineer", DOJ = new DateOnly(1991, 8, 23), IsActive = true },
74+
new Employee1 { Id = 102, Name = "Line", Designation = "Architect", DOJ = new DateOnly(1977, 1, 12), IsActive = true },
75+
new Employee1 { Id = 101, Name = "Daniel", Designation = "Architect", DOJ = new DateOnly(1977, 1, 12), IsActive = true },
76+
new Employee1 { Id = 108, Name = "Zayne", Designation = "Data Analyst", DOJ = new DateOnly(1991, 1, 1), IsActive = true },
77+
new Employee1 { Id = 109, Name = "Isha", Designation = "App Maker", DOJ = new DateOnly(1996, 7, 1), IsActive = true },
78+
};
79+
}
80+
81+
private Task OnSelectedItemsChanged(HashSet<Employee1> employees)
82+
{
83+
selectedEmployees = employees is not null && employees.Any() ? employees : new();
84+
return Task.CompletedTask;
85+
}
86+
87+
private Task SelectAllEmployees() => gridRef.SelectAllItemsAsync();
88+
89+
private Task UnselectAllEmployees() => gridRef.UnSelectAllItemsAsync();
90+
}

BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Selection_Documentation.razor

+12-2
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,21 @@
1919
<Demo Type="typeof(Grid_Demo_01_Selection)" Tabs="true" />
2020
</Section>
2121

22-
<Section Size="HeadingSize.H2" Name="Multiple Selection" PageUrl="@pageUrl" Link="multiple-selection">
22+
<Section Size="HeadingSize.H2" Name="Multiple selection" PageUrl="@pageUrl" Link="multiple-selection">
2323
<div class="mb-3">
2424
To select multiple rows, set <code>SelectionMode="GridSelectionMode.Multiple"</code>.
2525
</div>
26-
<Demo Type="typeof(Grid_Demo_02_Multiple_Selection)" Tabs="true" />
26+
<Demo Type="typeof(Grid_Demo_02_A_Multiple_Selection)" Tabs="true" />
27+
<Callout Color="CalloutColor.Danger" Heading="Note">
28+
<p>Selected items are removed from the selection if they are not rendered after paging, sorting, filtering, etc.</p>
29+
</Callout>
30+
</Section>
31+
32+
<Section Size="HeadingSize.H2" Name="Select all or unselect all programmatically" PageUrl="@pageUrl" Link="select-all-or-unselect-all-programmatically">
33+
<div class="mb-3">
34+
<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>.
35+
</div>
36+
<Demo Type="typeof(Grid_Demo_02_B_Multiple_Selection_Programmatically)" Tabs="true" />
2737
<Callout Color="CalloutColor.Danger" Heading="Note">
2838
<p>Selected items are removed from the selection if they are not rendered after paging, sorting, filtering, etc.</p>
2939
</Callout>

blazorbootstrap/Components/Grid/Grid.razor.cs

+57-31
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ public partial class Grid<TItem> : BlazorBootstrapComponentBase
1212

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

15+
private GridDetailView<TItem>? detailView;
16+
17+
public GridEmptyDataTemplate<TItem>? emptyDataTemplate;
18+
1519
/// <summary>
1620
/// Current grid state (filters, paging, sorting).
1721
/// </summary>
@@ -21,18 +25,14 @@ public partial class Grid<TItem> : BlazorBootstrapComponentBase
2125

2226
private RenderFragment? headerSelectionTemplate;
2327

24-
private GridDetailView<TItem>? detailView;
25-
26-
public GridEmptyDataTemplate<TItem>? emptyDataTemplate;
27-
28-
public GridLoadingTemplate<TItem>? loadingTemplate;
29-
3028
private bool isFirstRenderComplete = false;
3129

3230
private List<TItem>? items = null;
3331

3432
private object? lastAssignedDataOrDataProvider;
3533

34+
public GridLoadingTemplate<TItem>? loadingTemplate;
35+
3636
private int pageSize;
3737

3838
private bool requestInProgress = false;
@@ -69,7 +69,8 @@ protected override void OnInitialized()
6969

7070
protected override Task OnParametersSetAsync()
7171
{
72-
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.");
72+
if ((Data is null && DataProvider is null) || (Data is not null && DataProvider is not null))
73+
throw new ArgumentException($"Grid requires either {nameof(Data)} or {nameof(DataProvider)}, but not both or neither.");
7374

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

127+
public Task SelectAllItemsAsync() => SelectAllItemsInternalAsync(true);
128+
129+
public Task UnSelectAllItemsAsync() => SelectAllItemsInternalAsync(false);
130+
126131
internal void AddColumn(GridColumn<TItem> column) => columns.Add(column);
127132

128133
internal async Task FilterChangedAsync()
@@ -158,13 +163,13 @@ internal async Task RefreshDataAsync(bool firstRender = false, CancellationToken
158163
await LoadGridSettingsAsync();
159164

160165
var request = new GridDataProviderRequest<TItem>
161-
{
162-
PageNumber = AllowPaging ? gridCurrentState.PageIndex : 0,
163-
PageSize = AllowPaging ? pageSize : 0,
164-
Sorting = AllowSorting ? gridCurrentState.Sorting ?? GetDefaultSorting()! : null!,
165-
Filters = AllowFiltering ? GetFilters()! : null!,
166-
CancellationToken = cancellationToken
167-
};
166+
{
167+
PageNumber = AllowPaging ? gridCurrentState.PageIndex : 0,
168+
PageSize = AllowPaging ? pageSize : 0,
169+
Sorting = AllowSorting ? gridCurrentState.Sorting ?? GetDefaultSorting()! : null!,
170+
Filters = AllowFiltering ? GetFilters()! : null!,
171+
CancellationToken = cancellationToken
172+
};
168173

169174
GridDataProviderResult<TItem> result = default!;
170175

@@ -205,6 +210,12 @@ internal async ValueTask ResetPageNumberAsync(bool refreshGrid = false)
205210
await RefreshDataAsync(false);
206211
}
207212

213+
internal void SetGridDetailView(GridDetailView<TItem> detailView) => this.detailView = detailView;
214+
215+
internal void SetGridEmptyDataTemplate(GridEmptyDataTemplate<TItem> emptyDataTemplate) => this.emptyDataTemplate = emptyDataTemplate;
216+
217+
internal void SetGridLoadingTemplate(GridLoadingTemplate<TItem> loadingTemplate) => this.loadingTemplate = loadingTemplate;
218+
208219
internal async Task SortingChangedAsync(GridColumn<TItem> column)
209220
{
210221
if (columns == null || !columns.Any())
@@ -380,13 +391,8 @@ private async Task LoadGridSettingsAsync()
380391

381392
private async Task OnHeaderCheckboxChanged(ChangeEventArgs args)
382393
{
383-
allItemsSelected = bool.TryParse(args?.Value?.ToString(), out var checkboxState) && checkboxState;
384-
selectedItems = allItemsSelected ? new HashSet<TItem>(items!) : new HashSet<TItem>();
385-
SelectedItemsCount = selectedItems.Count;
386-
await CheckOrUnCheckAll();
387-
388-
if (SelectedItemsChanged.HasDelegate)
389-
await SelectedItemsChanged.InvokeAsync(selectedItems);
394+
var headerCheckboxState = bool.TryParse(args?.Value?.ToString(), out var checkboxState) && checkboxState;
395+
await SelectAllItemsInternalAsync(headerCheckboxState);
390396
}
391397

392398
private async Task OnPageChangedAsync(int newPageNumber)
@@ -495,13 +501,27 @@ private Task SaveGridSettingsAsync()
495501
return GridSettingsChanged.InvokeAsync(settings);
496502
}
497503

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

500-
internal void SetGridDetailView(GridDetailView<TItem> detailView) => this.detailView = detailView;
509+
allItemsSelected = selectAll;
510+
selectedItems = allItemsSelected ? new HashSet<TItem>(items!) : new HashSet<TItem>();
511+
SelectedItemsCount = allItemsSelected ? selectedItems.Count : 0;
501512

502-
internal void SetGridEmptyDataTemplate(GridEmptyDataTemplate<TItem> emptyDataTemplate) => this.emptyDataTemplate = emptyDataTemplate;
513+
if (allItemsSelected)
514+
await SetCheckboxStateAsync(headerCheckboxId, CheckboxState.Checked);
515+
else
516+
await SetCheckboxStateAsync(headerCheckboxId, CheckboxState.Unchecked);
503517

504-
internal void SetGridLoadingTemplate(GridLoadingTemplate<TItem> loadingTemplate) => this.loadingTemplate = loadingTemplate;
518+
await CheckOrUnCheckAll();
519+
520+
if (SelectedItemsChanged.HasDelegate)
521+
await SelectedItemsChanged.InvokeAsync(selectedItems);
522+
}
523+
524+
private async Task SetCheckboxStateAsync(string id, CheckboxState checkboxState) => await JSRuntime.InvokeVoidAsync("window.blazorBootstrap.grid.setSelectAllCheckboxState", id, (int)checkboxState);
505525

506526
/// <summary>
507527
/// Set filters.
@@ -534,9 +554,11 @@ private void SetFilters(IEnumerable<FilterItem> filterItems)
534554
#region Properties, Indexers
535555

536556
protected override string? ClassNames =>
537-
BuildClassNames(Class,
557+
BuildClassNames(
558+
Class,
538559
("bb-table", true),
539-
(BootstrapClass.TableSticky, FixedHeader));
560+
(BootstrapClass.TableSticky, FixedHeader)
561+
);
540562

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

707729
private string? GridContainerClassNames =>
708-
BuildClassNames(GridContainerClass,
709-
(BootstrapClass.TableResponsive, Responsive));
730+
BuildClassNames(
731+
GridContainerClass,
732+
(BootstrapClass.TableResponsive, Responsive)
733+
);
710734

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

717741
private string? GridContainerStyleNames =>
718-
BuildStyleNames(GridContainerStyle,
719-
($"height:{Height.ToString(CultureInfo.InvariantCulture)}{Unit.ToCssString()}", FixedHeader));
742+
BuildStyleNames(
743+
GridContainerStyle,
744+
($"height:{Height.ToString(CultureInfo.InvariantCulture)}{Unit.ToCssString()}", FixedHeader)
745+
);
720746

721747
/// <summary>
722748
/// This event is fired when the grid state is changed.

0 commit comments

Comments
 (0)