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>
关键注意点
- 模块声明:别忘了在你的Angular模块中声明
MenuFilterPipe,否则组件会找不到这个Pipe:@NgModule({ declarations: [LayoutComponent, MenuFilterPipe], // ...其他配置 }) export class YourModule { } - 多层兼容:这个Pipe是递归实现的,不管你的菜单有多少层级(3层、4层...),都能正确处理
- 大小写不敏感:搜索会自动转成小写,符合用户的常规搜索习惯
内容的提问来源于stack exchange,提问作者Shehzad
相关产品推荐
相关产品推荐

