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

Angular 4自定义Pipe实现嵌套JSON数组侧边栏菜单过滤

Angular多层菜单搜索过滤Pipe实现(含父项保留)

我完全懂你的痛点——当前的filter pipe只能处理顶层菜单,而且没法在子项匹配关键词时自动保留父级菜单,这对于嵌套结构的侧边栏来说根本满足不了需求。咱们一步步把这个问题解决掉。

先说说现有Pipe的问题

你的原始Pipe只做了两件事:检查顶层菜单项的title是否匹配,以及返回原数组/空数组。它完全没考虑:

  • 菜单的多层嵌套结构(比如Configuration下的子菜单,子菜单还有下一级)
  • 当子项匹配时,必须保留父级菜单,否则用户找不到匹配项所在的分组

改进后的自定义Pipe

这个Pipe会递归遍历所有层级的菜单,不仅保留匹配的菜单项,还会自动保留它们的父级节点:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'menuFilter'
})
export class MenuFilterPipe implements PipeTransform {
  transform(items: any[], searchText: string): any[] {
    // 边界处理:无数据或无搜索词时返回原数组
    if (!items || !searchText) return items;

    const lowerSearchText = searchText.toLowerCase();

    // 递归处理单个菜单项的核心函数
    const filterMenuItem = (item: any): any => {
      // 检查当前项标题是否匹配
      const isCurrentMatch = item.title.toLowerCase().includes(lowerSearchText);

      // 递归过滤子项(如果有)
      let filteredChildren = [];
      if (item.children && item.children.length > 0) {
        filteredChildren = item.children
          .map(child => filterMenuItem(child))
          .filter(Boolean); // 过滤掉不匹配的子项
      }

      // 只要当前项匹配,或者有匹配的子项,就保留这个节点
      if (isCurrentMatch || filteredChildren.length > 0) {
        return {
          ...item,
          children: filteredChildren // 替换为过滤后的子项集合
        };
      }

      // 无匹配则返回null,后续会被过滤
      return null;
    };

    // 处理所有顶层项,过滤掉无效的null值
    return items.map(item => filterMenuItem(item)).filter(Boolean);
  }
}

调整视图代码适配新Pipe

原视图有个小bug(<a>标签未闭合),同时我们要适配过滤后的菜单结构,确保父项和匹配子项都能正确渲染:

<input type="text" class="form-control" [(ngModel)]="searchText" placeholder="Search in menu">
<div class="sidebar">
  <nav class="sidebar-nav">
    <ul *ngFor="let menu of menu_items | menuFilter : searchText" class="nav">
      <!-- 顶层菜单标题 -->
      <li class="nav-title text-center">
        <span>{{menu.title}}</span>
      </li>
      <!-- 第一层子菜单 -->
      <li *ngFor="let child of menu.children" class="{{menu.class}}">
        <a *ngIf="!child.children" class="nav-link" href="javascript:void(0)">
          <i class="{{child.iconClass}}"></i> {{child.title}}
          <span *ngIf="child.new" class="badge badge-info">NEW</span>
        </a>
        <!-- 第二层子菜单(仅当有匹配子项时渲染) -->
        <ul *ngIf="child.children && child.children.length > 0" class="nav-dropdown-items">
          <li class="nav-item" *ngFor="let childlevel2 of child.children">
            <a class="nav-link" href="javascript:void(0)">
              <i class="{{childlevel2.iconClass}}"></i> {{childlevel2.title}}
            </a>
          </li>
        </ul>
      </li>
    </ul>
  </nav>
</div>

关键注意点

  1. 模块声明:别忘了在你的Angular模块中声明MenuFilterPipe,否则组件会找不到这个Pipe:
    @NgModule({
      declarations: [LayoutComponent, MenuFilterPipe],
      // ...其他配置
    })
    export class YourModule { }
    
  2. 多层兼容:这个Pipe是递归实现的,不管你的菜单有多少层级(3层、4层...),都能正确处理
  3. 大小写不敏感:搜索会自动转成小写,符合用户的常规搜索习惯

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:11:37