如何获取Razor递归组件中所有选中的复选框列表?
递归复选框组件:获取所有选中项解决方案
已实现从服务获取递归结构对象列表,生成带展开/折叠功能的递归复选框组件,当前需要完成收集所有选中项列表的功能,用于后续服务调用或表单提交。以下是原代码及修改方案:
原代码
YourComponent.razor
@page "/yourcomponent" <div style="margin-left: 20px;"> @foreach (var item in RootItems2()) { <RecursiveExpandableWithCheckbox Title="@item.Title" ChildItems="@item.ChildItems" /> } </div> @code { Dashboard.Utility.DashboardExtras dashboardExtras = new DashboardExtras(); private List<ExpandableItemModel> RootItems2() { try { string theRootObjectId = dashboardExtras.GetTheRootObjectId(); Task<List<Item>> items = dashboardExtras.GetItemChildrenByObjectId(theRootObjectId); List<ExpandableItemModel> rootItems = new List<ExpandableItemModel>(); foreach (var i0 in items.Result) { rootItems.Add(new ExpandableItemModel { Title = i0.Name + " - " + i0.FQID.ObjectId, ChildItems = RootItems666(i0.FQID.ObjectId.ToString()) }); } return rootItems; } catch (Exception ex) { return null; } } private List<ExpandableItemModel> RootItems666(string s) { Task<List<Item>> items = dashboardExtras.GetItemChildrenByObjectId(s); List<ExpandableItemModel> rootItems = new List<ExpandableItemModel>(); if (items.Result != null) { foreach (var i0 in items.Result) { rootItems.Add(new ExpandableItemModel { Title = i0.Name + " - " + i0.FQID.ObjectId, ChildItems = RootItems666(i0.FQID.ObjectId.ToString()) }); } return rootItems; } else { return null; } } }
RecursiveExpandableWithCheckbox.razor
<div> <span @onclick="ToggleCollapse">@GetExpandIcon()</span> <input type="checkbox" @bind="IsSelected" @oninput="OnCheckboxValueChanged" /> <!-- Checkbox --> <span>@Title</span> </div> @if (!IsCollapsed && ChildItems != null) { <div style="margin-left: 20px;"> @foreach (var item in ChildItems) { <RecursiveExpandableWithCheckbox Title="@item.Title" ChildItems="@item.ChildItems" @bind-IsSelected="@item.IsSelected" /> } </div> } @code { [Parameter] public string Title { get; set; } [Parameter] public List<ExpandableItemModel> ChildItems { get; set; } [Parameter] public bool IsSelected { get; set; } = false; [Parameter] public EventCallback<bool> IsSelectedChanged { get; set; } [Parameter] public EventCallback<string> StringList { get; set; } private List<string> SelectedOnes = new List<string>(); private bool IsCollapsed = true; private void ToggleCollapse() { IsCollapsed = !IsCollapsed; } private string GetExpandIcon() { return IsCollapsed ? "+" : "-"; } private async Task OnCheckboxValueChanged(ChangeEventArgs e) { IsSelected = (bool)e.Value; if (ChildItems != null) { foreach (var item in ChildItems) { item.IsSelected = IsSelected; } } await IsSelectedChanged.InvokeAsync(IsSelected); } }
修改方案
1. 完善数据模型
给ExpandableItemModel添加唯一标识字段,方便后续精准获取选中项:
public class ExpandableItemModel { public string Title { get; set; } public string ObjectId { get; set; } // 存储唯一ID,从原Title中提取 public List<ExpandableItemModel> ChildItems { get; set; } public bool IsSelected { get; set; } = false; }
2. 修正父组件异步数据加载
原代码用Task.Result会导致死锁,改为异步初始化加载数据,同时添加选中项收集逻辑:
@page "/yourcomponent" @inject Dashboard.Utility.DashboardExtras dashboardExtras <div style="margin-left: 20px;"> @if (_rootItems == null) { <p>加载中...</p> } else { @foreach (var item in _rootItems) { <RecursiveExpandableWithCheckbox Model="@item" /> } } </div> <button @onclick="GetSelectedItems">获取选中项</button> <p>选中项ID:@string.Join(", ", _selectedObjectIds)</p> @code { private List<ExpandableItemModel> _rootItems; private List<string> _selectedObjectIds = new List<string>(); protected override async Task OnInitializedAsync() { try { string theRootObjectId = dashboardExtras.GetTheRootObjectId(); var items = await dashboardExtras.GetItemChildrenByObjectId(theRootObjectId); _rootItems = await BuildRecursiveItems(items); } catch (Exception ex) { // 处理异常,比如显示错误信息 } } private async Task<List<ExpandableItemModel>> BuildRecursiveItems(List<Item> items) { var result = new List<ExpandableItemModel>(); if (items != null) { foreach (var i0 in items) { var childItems = await dashboardExtras.GetItemChildrenByObjectId(i0.FQID.ObjectId.ToString()); result.Add(new ExpandableItemModel { Title = $"{i0.Name} - {i0.FQID.ObjectId}", ObjectId = i0.FQID.ObjectId.ToString(), ChildItems = await BuildRecursiveItems(childItems) }); } } return result; } private void GetSelectedItems() { _selectedObjectIds.Clear(); CollectSelectedItems(_rootItems, _selectedObjectIds); } private void CollectSelectedItems(List<ExpandableItemModel> items, List<string> selectedIds) { if (items == null) return; foreach (var item in items) { if (item.IsSelected) { selectedIds.Add(item.ObjectId); } CollectSelectedItems(item.ChildItems, selectedIds); } } }
3. 优化递归组件逻辑
修改组件直接接收ExpandableItemModel,简化参数传递,同时完善选中联动(包括子项选中时自动更新父项半选状态):
<div> <span @onclick="ToggleCollapse">@GetExpandIcon()</span> <input type="checkbox" @bind="Model.IsSelected" @onchange="OnCheckboxChanged" indeterminate="@IsIndeterminate" /> <span>@Model.Title</span> </div> @if (!IsCollapsed && Model.ChildItems != null && Model.ChildItems.Any()) { <div style="margin-left: 20px;"> @foreach (var child in Model.ChildItems) { <RecursiveExpandableWithCheckbox Model="@child" /> } </div> } @code { [Parameter, Required] public ExpandableItemModel Model { get; set; } private bool IsCollapsed = true; private bool IsIndeterminate => Model.ChildItems?.Any(c => c.IsSelected) == true && !Model.IsSelected; private void ToggleCollapse() { IsCollapsed = !IsCollapsed; } private string GetExpandIcon() { return IsCollapsed ? "+" : "-"; } private void OnCheckboxChanged(ChangeEventArgs e) { var isSelected = (bool)e.Value; Model.IsSelected = isSelected; // 子项同步选中状态 if (Model.ChildItems != null) { foreach (var child in Model.ChildItems) { SetChildSelection(child, isSelected); } } StateHasChanged(); } private void SetChildSelection(ExpandableItemModel item, bool isSelected) { item.IsSelected = isSelected; if (item.ChildItems != null) { foreach (var child in item.ChildItems) { SetChildSelection(child, isSelected); } } } protected override void OnParametersSet() { // 监听子项变化,更新当前项的半选状态 if (Model.ChildItems != null) { var hasSelectedChild = Model.ChildItems.Any(c => c.IsSelected); var allSelected = Model.ChildItems.All(c => c.IsSelected); if (hasSelectedChild && !allSelected) { StateHasChanged(); } } } }
核心说明
- 用异步加载数据替代同步
Task.Result,避免UI线程死锁 - 通过递归遍历数据模型收集所有选中项的ObjectId,无需依赖组件回调传递
- 新增半选状态(indeterminate),当部分子项选中时父项显示半选,提升用户体验
- 组件直接绑定数据模型,简化参数传递,状态变化实时同步到模型中
内容的提问来源于stack exchange,提问作者RichardCooke
相关产品推荐
相关产品推荐

