diff --git a/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Demo_01_Selection.razor b/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Demo_01_A_Selection.razor similarity index 92% rename from BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Demo_01_Selection.razor rename to BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Demo_01_A_Selection.razor index 35b337d05..85bd2d09c 100644 --- a/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Demo_01_Selection.razor +++ b/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Demo_01_A_Selection.razor @@ -2,7 +2,7 @@ Class="table table-hover table-bordered" DataProvider="EmployeesDataProvider" AllowSelection="true" - SelectedItemsChanged="OnSelectedItemsChanged" + @bind-SelectedItems="@selectedEmployees" Responsive="true"> @@ -69,10 +69,4 @@ new Employee1 { Id = 109, Name = "Isha", Designation = "App Maker", DOJ = new DateOnly(1996, 7, 1), IsActive = true }, }; } - - private Task OnSelectedItemsChanged(HashSet employees) - { - selectedEmployees = employees is not null && employees.Any() ? employees : new(); - return Task.CompletedTask; - } } \ No newline at end of file diff --git a/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Demo_01_B_Default_Selection.razor b/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Demo_01_B_Default_Selection.razor new file mode 100644 index 000000000..95275229c --- /dev/null +++ b/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Demo_01_B_Default_Selection.razor @@ -0,0 +1,74 @@ + + + + + @context.Id + + + @context.Name + + + @context.Designation + + + @context.DOJ + + + @context.IsActive + + + + + +
+ Selected Items Count: @selectedEmployees.Count +
+ +
+ Selected Employees: +
    + @foreach (var emp in selectedEmployees) + { +
  • @emp.Name
  • + } +
+
+ +@code { + private IEnumerable employees = default!; + + private HashSet selectedEmployees = new(){ + new Employee1 { Id = 102, Name = "Line", Designation = "Architect", DOJ = new DateOnly(1977, 1, 12), IsActive = true } + }; + + private async Task> EmployeesDataProvider(GridDataProviderRequest 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 GetEmployees() + { + return new List + { + 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 }, + }; + } +} \ No newline at end of file diff --git a/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Demo_02_A_Multiple_Selection.razor b/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Demo_02_A_Multiple_Selection.razor index caf8cab3b..503717be0 100644 --- a/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Demo_02_A_Multiple_Selection.razor +++ b/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Demo_02_A_Multiple_Selection.razor @@ -4,7 +4,7 @@ AllowFiltering="true" AllowSelection="true" SelectionMode="GridSelectionMode.Multiple" - SelectedItemsChanged="OnSelectedItemsChanged" + @bind-SelectedItems="@selectedEmployees" Responsive="true"> @@ -44,7 +44,10 @@ @code { private IEnumerable employees = default!; - private HashSet selectedEmployees = new(); + private HashSet selectedEmployees = new(){ + 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 }, + }; private async Task> EmployeesDataProvider(GridDataProviderRequest request) { @@ -71,10 +74,4 @@ new Employee1 { Id = 109, Name = "Isha", Designation = "App Maker", DOJ = new DateOnly(1996, 7, 1), IsActive = true }, }; } - - private Task OnSelectedItemsChanged(HashSet employees) - { - selectedEmployees = employees is not null && employees.Any() ? employees : new(); - return Task.CompletedTask; - } } \ No newline at end of file diff --git a/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Demo_02_C_Multiple_Selection_Default_Programmatically.razor b/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Demo_02_C_Multiple_Selection_Default_Programmatically.razor new file mode 100644 index 000000000..8183601f0 --- /dev/null +++ b/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Demo_02_C_Multiple_Selection_Default_Programmatically.razor @@ -0,0 +1,90 @@ +
+ + +
+ + + + + + @context.Id + + + @context.Name + + + @context.Designation + + + @context.DOJ + + + @context.IsActive + + + + + +
+ Selected Items Count: @selectedEmployees.Count +
+ +
+ Selected Employees: +
    + @foreach (var emp in selectedEmployees) + { +
  • @emp.Name
  • + } +
+
+ +@code { + private Grid gridRef; + private IEnumerable employees = default!; + + private HashSet selectedEmployees = new(); + + private async Task> EmployeesDataProvider(GridDataProviderRequest 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 GetEmployees() + { + return new List + { + 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 employees) + { + selectedEmployees = employees is not null && employees.Any() ? employees : new(); + return Task.CompletedTask; + } + + private Task SelectAllEmployees() => gridRef.SelectAllItemsAsync(); + + private Task UnselectAllEmployees() => gridRef.UnSelectAllItemsAsync(); +} \ No newline at end of file diff --git a/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Selection_Documentation.razor b/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Selection_Documentation.razor index 1fcd44c02..3ac1e7678 100644 --- a/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Selection_Documentation.razor +++ b/BlazorBootstrap.Demo.RCL/Components/Pages/Grid/06-selection/Grid_Selection_Documentation.razor @@ -16,7 +16,15 @@ Set AllowSelection="true" to enable the selection on the Grid. By default, SelectionMode is Single. - + + + +
+
+ Set AllowSelection="true" to enable the selection on the Grid. + By default, SelectionMode is Single. +
+
diff --git a/blazorbootstrap/Components/Grid/Grid.razor.cs b/blazorbootstrap/Components/Grid/Grid.razor.cs index ee757b060..2474f2cb9 100644 --- a/blazorbootstrap/Components/Grid/Grid.razor.cs +++ b/blazorbootstrap/Components/Grid/Grid.razor.cs @@ -35,12 +35,12 @@ public partial class Grid : BlazorBootstrapComponentBase private int pageSize; + private Queue> queuedTasks = new(); + private bool requestInProgress = false; private HashSet selectedItems = new(); - public int SelectedItemsCount = 0; - private int? totalCount = null; #endregion @@ -56,6 +56,18 @@ protected override async Task OnAfterRenderAsync(bool firstRender) } await base.OnAfterRenderAsync(firstRender); + + // process queued tasks + while (true) + { + if (queuedTasks.Count == 0) + break; + + var taskToExecute = queuedTasks.Dequeue(); + + if (taskToExecute is not null) + await taskToExecute.Invoke(); + } } protected override void OnInitialized() @@ -63,6 +75,7 @@ protected override void OnInitialized() headerCheckboxId = IdUtility.GetNextId(); pageSize = PageSize; + selectedItems = SelectedItems!; base.OnInitialized(); } @@ -93,6 +106,12 @@ protected override Task OnParametersSetAsync() SaveGridSettingsAsync(); } + //if (!mustRefreshData && selectedItems != SelectedItems) + //{ + // mustRefreshData = true; + // SelectedItems = selectedItems; + //} + // We want to trigger the first data load when we've collected the initial set of columns // because they might perform some action, like setting the default sort order. // It would be wasteful to have to re-query immediately. @@ -163,13 +182,13 @@ internal async Task RefreshDataAsync(bool firstRender = false, CancellationToken await LoadGridSettingsAsync(); var request = new GridDataProviderRequest - { - 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 result = default!; @@ -192,9 +211,7 @@ internal async Task RefreshDataAsync(bool firstRender = false, CancellationToken if (AllowSelection) { PrepareCheckboxIds(); - - if (!firstRender) - await RefreshSelectionAsync(); + await RefreshSelectionAsync(); } requestInProgress = false; @@ -354,7 +371,7 @@ private int GetTotalPagesCount() /// /// /// bool - private bool IsItemSelected(TItem item) => selectedItems.Contains(item); + private bool IsItemSelected(TItem item) => selectedItems?.Contains(item) ?? false; private async Task LoadGridSettingsAsync() { @@ -417,12 +434,11 @@ private async Task OnRowCheckboxChanged(string id, TItem item, ChangeEventArgs a if (SelectionMode == GridSelectionMode.Multiple) { _ = isChecked ? selectedItems.Add(item) : selectedItems.Remove(item); - SelectedItemsCount = selectedItems.Count; - allItemsSelected = SelectedItemsCount == (items?.Count ?? 0); + allItemsSelected = (selectedItems.Count == (items?.Count ?? 0)); if (allItemsSelected) await SetCheckboxStateAsync(headerCheckboxId, CheckboxState.Checked); - else if (SelectedItemsCount == 0) + else if (selectedItems.Count == 0) await SetCheckboxStateAsync(headerCheckboxId, CheckboxState.Unchecked); else await SetCheckboxStateAsync(headerCheckboxId, CheckboxState.Indeterminate); @@ -430,7 +446,6 @@ private async Task OnRowCheckboxChanged(string id, TItem item, ChangeEventArgs a else { selectedItems = isChecked ? new HashSet { item } : new HashSet(); - SelectedItemsCount = selectedItems.Count; allItemsSelected = false; await CheckOrUnCheckAll(); await SetCheckboxStateAsync(id, isChecked ? CheckboxState.Checked : CheckboxState.Unchecked); @@ -438,6 +453,8 @@ private async Task OnRowCheckboxChanged(string id, TItem item, ChangeEventArgs a if (SelectedItemsChanged.HasDelegate) await SelectedItemsChanged.InvokeAsync(selectedItems); + else + SelectedItems = selectedItems; } private async Task OnScroll(EventArgs e) @@ -465,18 +482,19 @@ private async Task RefreshSelectionAsync() ? new HashSet() : selectedItems?.Intersect(items!).ToHashSet() ?? new HashSet(); - SelectedItemsCount = selectedItems.Count; - allItemsSelected = SelectedItemsCount > 0 && items!.Count == SelectedItemsCount; + allItemsSelected = selectedItems.Count > 0 && items!.Count == selectedItems.Count; if (allItemsSelected) await SetCheckboxStateAsync(headerCheckboxId, CheckboxState.Checked); - else if (SelectedItemsCount > 0) + else if (selectedItems.Count > 0) await SetCheckboxStateAsync(headerCheckboxId, CheckboxState.Indeterminate); else await SetCheckboxStateAsync(headerCheckboxId, CheckboxState.Unchecked); if (SelectedItemsChanged.HasDelegate) await SelectedItemsChanged.InvokeAsync(selectedItems); + else + SelectedItems = selectedItems; } private async Task RowClick(TItem item, EventArgs args) @@ -503,12 +521,11 @@ private Task SaveGridSettingsAsync() private async Task SelectAllItemsInternalAsync(bool selectAll) { - if(SelectionMode != GridSelectionMode.Multiple) + if (SelectionMode != GridSelectionMode.Multiple) return; allItemsSelected = selectAll; selectedItems = allItemsSelected ? new HashSet(items!) : new HashSet(); - SelectedItemsCount = allItemsSelected ? selectedItems.Count : 0; if (allItemsSelected) await SetCheckboxStateAsync(headerCheckboxId, CheckboxState.Checked); @@ -519,9 +536,16 @@ private async Task SelectAllItemsInternalAsync(bool selectAll) if (SelectedItemsChanged.HasDelegate) await SelectedItemsChanged.InvokeAsync(selectedItems); + else + SelectedItems = selectedItems; } - private async Task SetCheckboxStateAsync(string id, CheckboxState checkboxState) => await JSRuntime.InvokeVoidAsync("window.blazorBootstrap.grid.setSelectAllCheckboxState", id, (int)checkboxState); + private Task SetCheckboxStateAsync(string id, CheckboxState checkboxState) + { + queuedTasks.Enqueue(async () => await JSRuntime.InvokeVoidAsync("window.blazorBootstrap.grid.setSelectAllCheckboxState", id, (int)checkboxState)); + + return Task.CompletedTask; + } /// /// Set filters. @@ -898,6 +922,12 @@ private void SetFilters(IEnumerable filterItems) [Parameter] public Func? RowClass { get; set; } + /// + /// Gets or sets the selected items. + /// + [Parameter] + public HashSet? SelectedItems { get; set; } + /// /// This event is fired when the item selection changes. /// diff --git a/blazorbootstrap/Components/ThemeSwitcher/ThemeSwitcherJsInterop.cs b/blazorbootstrap/Components/ThemeSwitcher/ThemeSwitcherJsInterop.cs index e9e6f1d14..50f8f9c2d 100644 --- a/blazorbootstrap/Components/ThemeSwitcher/ThemeSwitcherJsInterop.cs +++ b/blazorbootstrap/Components/ThemeSwitcher/ThemeSwitcherJsInterop.cs @@ -21,10 +21,17 @@ public ThemeSwitcherJsInterop(IJSRuntime jsRuntime) public async ValueTask DisposeAsync() { - if (moduleTask.IsValueCreated) + try { - var module = await moduleTask.Value; - await module.DisposeAsync(); + if (moduleTask.IsValueCreated) + { + var module = await moduleTask.Value; + await module.DisposeAsync(); + } + } + catch (JSDisconnectedException) + { + // do nothing } }