Exploring deep within a menu using recursive search and filtering techniques

I am currently working on an Angular app and facing a challenge related to the navigation menu. The menu includes a search option for filtering. Below is the JSON structure for the menu:

menuItems: [
{
    name: "Dashboard",
    iconPath: "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="105471637850727f7162743e607e77">[email protected]</a>",
    routingPath: "",
    children: [
        {
            name: "Version 1",
            iconPath: "",
            routingPath: "",
            children: [
                {
                    name: "Version 1.1 abc",
                    iconPath: "",
                    routingPath: "",
                    children: [
                        {
                            name: "Version 1.1.1 ccc",
                            iconPath: "",
                            routingPath: "",
                            children: [
                                {
                                    name: "Version 1.1.1.1 hello",
                                    iconPath: "",
                                    routingPath: "",
                                    children: []
                                },
                                ...

This example demonstrates the menu structure. My goal is to display specific menu items based on a search query like "hello," showcasing all relevant data within the object:

https://i.sstatic.net/41chQ.jpg

However, my current solution is not yielding the desired outcome. I've attempted the following code:

private performFiltering() {
    const searchValue: string = this.searchBarInput.nativeElement.value.toLowerCase();

    if (searchValue.length > 0) {
        ...
}

private recursiveSearchInMenu(menu: MenuItemModel, searchValue) {
    let found = menu.children.find((item) => item.name.toLowerCase().includes(searchValue));
    ...
}

The outcome of my implementation can be seen here: https://i.sstatic.net/qaTKr.jpg

If you have any suggestions or ideas on how to improve this search functionality, please share them. Thank you!

Answer №1

It seems that the issue with your code lies in the fact that your current approach will only filter the parent object (Dashboard) and not its content. You may need to consider implementing a recursive function like recursiveSearchInMenu, which can create an array of filtered options while also keeping track of any child objects that are found. This is just one way to tackle the problem.

private recursiveSearchInMenu(menu: MenuItemModel, searchValue) {
    let result = [];
    let found = menu.children.find((item) => item.name.toLowerCase().includes(searchValue));
    if(found)
    {
        result.add(found);
    }else {
        let i = 0;
        while (!found && i < menu.children.length) {
            if (menu.children[i].children && menu.children[i].children.length) {
                found = this.recursiveSearchInMenu(menu.children[i], searchValue);  
                if(found.Length > 0) {
                    result.add({name: menu.children[i].name,
                    iconPath: menu.children[i].iconPath,
                    routingPath : menu.children[i].routingPath,
                    children: found})
                }
            i++;
         }
        }
    }
    return result;
}

Answer №2

Ah, I've cracked it...

Here's the current solution I'm working with:

private performFiltering(): boolean {
    const searchValue: string = this.searchBarInput.nativeElement.value.toLowerCase();
    const isFiltered = searchValue.length > 0;

    this.showSearchButton(isFiltered);

    if (isFiltered) {
        this.filteredMenu.menuItems = this.recursiveSearchInMenu(
            JSON.parse(JSON.stringify(this.fullMenu.menuItems)),
            searchValue
        );
    } else {
        this.filteredMenu.menuItems = this.fullMenu.menuItems;
    }

    return isFiltered;
}

private recursiveSearchInMenu(menuItems: MenuItemModel[], searchValue: string): MenuItemModel[] {
    const matchingMenuItems: MenuItemModel[] = [];

    menuItems.forEach((menuItem) => {
        if (menuItem.name.toLowerCase().includes(searchValue)) {
            matchingMenuItems.push(menuItem);
        } else {
            const matchingChildren = this.recursiveSearchInMenu(menuItem.children, searchValue);

            if (matchingChildren.length > 0) {
                menuItem.children = matchingChildren;
                matchingMenuItems.push(menuItem);
            }
        }
    });

    return matchingMenuItems;
}

If you have any insights on how to enhance this code, your feedback would be greatly appreciated.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Trigger a re-initialization when there is a change in the @

Can we instruct Angular to execute the init function (or any other lifecycle function or custom function) when the component input changes, or do we need to manually trigger that function from the parent component instead of modifying the input directly? ...

Setting up a 2017 Angular 2 seed project with XAMPP: A step-by-step

I completed the setup as instructed on the Angular website. What is the proper way to configure the node_modules for use with xampp as my server? ...

Navigating with Angular's routerLinkActive and managing query parameters

New Update as of March 2021 Exciting news! The Angular team has finally merged the PR for that feature. You can check out more details at https://github.com/angular/angular/pull/40303. To use this new feature, simply include the following code: <div r ...

Developing a function for filtering datasets based on dual conditions

Within my dataset, I am working with two categorical variables: Deciles (ranging from DC1 to DC10) and Population (consisting of SAP, TD6, and SH). https://i.sstatic.net/u8duJ.png I expect each decile (DC7, for example) to encompass all three levels of P ...

Empty dialog box displayed in production for Angular material datepicker

I'm facing an issue that I can't seem to figure out. The code works perfectly fine when I run it locally using ng build and ng serve. However, when I moved it to production, it started misbehaving. When I tried to replicate the issue in my local ...

The Typescript decorator is unable to access the property type within its own scope

I am currently in the process of developing a dependency injector for use in my VUE js project. Recently, I created an Inject decorator with the intention of accessing a property type. It was functioning perfectly fine yesterday, but now it seems that som ...

Guide on integrating a Single Page Application into an ASP.NET Core library and hosting it from a specific route

Scenario I am working on creating an aspnetcore library or module that will contain a small SPA frontend. The goal is to package the html/js/css files along with the dll, and serve the SPA from a specific path (/some-module) without the need for configura ...

Exploring the Power of Angular 2 with NgRx

Seeking advice from NgRx users. I have noticed a discrepancy between the official documentation and various articles and tutorials regarding whether to use classes for actions, reducers, etc. Which approach is considered more effective? Official documenta ...

Angular has not yet processed the MatTableDataSource

As a newcomer to angular material, I encountered an issue when attempting to bind the tablesource to mat-table. The compile error message is displayed below: "node_modules/@angular/material/table/table-data-source.d.ts:25:22 - error NG6002: Appears in ...

Using jQuery to add styling when the user is not on a particular page

I've been struggling to apply a unique style to my navigation bar only when I'm on any page that is not the index page. Unfortunately, no matter what I try, the desired result isn't showing up and I'm at a loss as to what the problem mi ...

Troubleshooting a deletion request in Angular Http that is returning undefined within the MEAN stack

I need to remove the refresh token from the server when the user logs out. auth.service.ts deleteToken(refreshToken:any){ return this.http.delete(`${environment.baseUrl}/logout`, refreshToken).toPromise() } header.component.ts refreshToken = localS ...

Issue with Adding Additional Property to react-leaflet Marker Component in TypeScript

I'm attempting to include an extra property count in the Marker component provided by react-leaflet. Unfortunately, we're encountering an error. Type '{ children: Element; position: [number, number]; key: number; count: number; }' is n ...

Organizing list items with interactive buttons on an Ionic grid

Struggling to format a list containing a label and two buttons in each item. Need assistance with the styling. Below is the code snippet and an image illustrating the desired outcome. <ion-content> <ion-list> <ion-item> <ion-labe ...

Attempting to build a table within a table structure

My goal is to create a nested table structure similar to this image: https://i.sstatic.net/v6lZo.png The number of months, topics, and arguments for each topic can vary as they are retrieved from a database. I have attempted to implement this functionali ...

Using Vue.js, learn how to target a specific clicked component and update its state accordingly

One of the challenges I'm facing is with a dropdown component that is used multiple times on a single page. Each dropdown contains various options, allowing users to select more than one option at a time. The issue arises when the page refreshes afte ...

AInspector WCAG evaluation found that mat-select does not meet level A compliance standards

As I work on making my website WCAG level A compliant, I've encountered an issue with the mat-select component in Angular material. After running checks with the AInspector extension for Firefox, it appears that the mat-select component lacks aria-con ...

How to effectively manage errors in TypeScript using RxJS?

Exploring subscribe arguments in the official RxJS documentation has raised some interesting questions for me. For instance, what is the purpose of using error: (e: string) => { ... } to catch errors emitted by an observable? Despite this implementation ...

What is the best layer to handle Entity-DTO conversion in my application?

I utilized TypeORM to establish two entities: User and School: @Entity() export class User { // ... @ManyToOne(() => School, school => school.id) school: School; // ... static from( uid: string, name: string, email: string, ...

TS2307: Module '@/*' and respective type declarations could not be located

My TypeScript webpack project was struggling to resolve all custom paths today for some unknown reason. Despite trying various solutions such as re-installing packages and modifying settings, the issue persists. The same error is displayed for all files. ...

Managing Visual Studio Code Extension Intellisense: A Guide

I am looking to create an extension I recommend using "CompletionList" for users. Users can trigger completion by running "editor.action.triggerSuggest" The process of my extensions is as follows: Users input text If they press the "completion command," ...