You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor递归分类菜单问题:仅展开选中分类的子菜单

解决Blazor递归菜单仅展开选中分类的问题

问题根源

你现在的问题出在用了全局的展开/折叠状态变量,导致点击任何分类都会触发所有菜单同步展开/折叠。递归菜单的核心是每个分类项必须维护自己独立的展开状态,不能共用同一个变量。

具体解决方案

1. 给分类添加独立的展开状态

有两种实现方式,选适合你的:

方案A:直接给分类模型加属性(最简单)

如果你的分类模型(比如Category)可以修改,新增一个IsExpanded属性:

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Category> SubCategories { get; set; } = new();
    // 每个分类自己的展开状态,默认折叠
    public bool IsExpanded { get; set; } = false;
}

方案B:用字典存储状态(不修改原有模型)

如果不想改动后端返回的模型,就在组件里用字典记录每个分类的展开状态:

// 键是分类ID,值是展开状态
private Dictionary<int, bool> _expandedStates = new();

protected override void OnInitialized()
{
    // 初始化所有分类为折叠状态,假设你的分类列表是Categories
    InitExpandedStates(Categories);
}

private void InitExpandedStates(List<Category> categories)
{
    foreach (var cat in categories)
    {
        if (!_expandedStates.ContainsKey(cat.Id))
            _expandedStates[cat.Id] = false;
        // 递归初始化子分类
        if (cat.SubCategories?.Any() == true)
            InitExpandedStates(cat.SubCategories);
    }
}

2. 修改递归渲染逻辑

用递归组件实现(推荐,代码更清晰)

新建一个CategoryItem.razor组件,专门渲染单个分类:

@* 接收当前要渲染的分类 *@
[Parameter] public Category CurrentCat { get; set; }

<div class="category-item">
    <button @onclick="ToggleExpand">
        @(CurrentCat.IsExpanded ? "▼" : "▶") @CurrentCat.Name
    </button>
    @* 只有当前分类展开且有子分类时,才渲染子菜单 *@
    @if (CurrentCat.IsExpanded && CurrentCat.SubCategories.Any())
    {
        <div style="padding-left: 20px;">
            @foreach (var subCat in CurrentCat.SubCategories)
            {
                <CategoryItem CurrentCat="subCat" />
            }
        </div>
    }
</div>

@code {
    private void ToggleExpand()
    {
        // 切换当前分类的展开状态
        CurrentCat.IsExpanded = !CurrentCat.IsExpanded;
    }
}

然后在父组件里调用这个递归组件:

<div class="category-container">
    @foreach (var cat in Categories)
    {
        <CategoryItem CurrentCat="cat" />
    }
</div>

@code {
    private List<Category> Categories { get; set; }

    // 从后端加载分类数据的逻辑
    protected override async Task OnInitializedAsync()
    {
        Categories = await YourBackendService.GetAllCategories();
    }
}

用字典状态的渲染方式(不用单独组件)

如果不想新建组件,直接在父组件里递归渲染:

@foreach (var cat in Categories)
{
    <RenderCategory Cat="cat" />
}

@code {
    private void RenderCategory(Category cat)
    {
        <div class="category-item">
            <button @onclick="() => ToggleCat(cat.Id)">
                @(_expandedStates[cat.Id] ? "▼" : "▶") @cat.Name
            </button>
            @if (_expandedStates[cat.Id] && cat.SubCategories.Any())
            {
                <div style="padding-left: 20px;">
                    @foreach (var subCat in cat.SubCategories)
                    {
                        RenderCategory(subCat);
                    }
                </div>
            }
        </div>
    }

    private void ToggleCat(int catId)
    {
        _expandedStates[catId] = !_expandedStates[catId];
    }
}

常见坑点排查

  • 绝对不要用全局的bool IsExpanded变量控制所有菜单,必须给每个分类单独分配状态。
  • 递归渲染时,子分类的状态要完全独立,不要继承父级的状态变量。
  • 如果用字典存储状态,确保每个分类的ID是唯一的,避免不同分类共享同一个状态。

内容的提问来源于stack exchange,提问作者MangoSamurai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 12:30:59