DEV Community

snackboy
snackboy

Posted on

Menu [Re]Factoring

While working on a new[er] side project, I was adding menu items to a sidebar navigation and realized the code could probably be simplified by using a bit of JSON. This particular project is in Angular, but the pattern is not Angular specific.

The menu in menu.component.html looks something like this:

<nav>
    <div class="nav-title">Recipes</div>
    <ul><li class="nav-item" [routerLink]="['/recipes']">
            <i class="bx bxs-food-menu"></i>
            <span>My Recipes</span>
        </li>
        <li class="nav-item" [routerLink]="['/edit-recipe/new']">
            <i class="bx bxs-plus-circle"></i>
            <span>New Recipe</span>
        </li>
        <li class="nav-item" [routerLink]="['/grocerylists']">
            <i class="bx bxs-shopping-bag"></i>
            <span>Grocery Lists</span>
        </li>
        <li class="nav-item">
            <i class="bx bxs-search"></i>
            <span>Search</span>
        </li>
        <li class="nav-item">
            <i class="bx bxs-help-circle"></i>
            <span>Help</span>
        </li>
    </ul>
    <div class="nav-title">Account Management</div>
    <ul><li class="nav-item">
            <i class="bx bxs-user"></i>
            <span>Profile</span>
        </li>
        <li class="nav-item">
            <i class="bx bx-home-heart"></i>
            <span>Support Us</span>
        </li>
        <li class="nav-item">
            <i class="bx bx-support"></i>
            <span>Contact Us</span>
        </li>
        <li class="nav-item" (click)="logout()">
            <i class="bx bx-log-out-circle"></i>
            <span>Logout</span>
        </li>
    </ul> 
</nav>
Enter fullscreen mode Exit fullscreen mode

Generally, I try not to abstract unnecessarily. But in this case, given the simplicity of the routing and icons, and not knowing what the future might hold, putting the menu into JSON and iterating through it might be the way to go.

Reviewing the current menu, there are two sections: Recipes and Account Management. So in menu.component.ts, the following array has been added:

    public menu: Array<any> = [
        { section: 'Recipes' },
        { section: 'Account Managment'}
    ]
Enter fullscreen mode Exit fullscreen mode

The corresponding html template now looks like this:

    <nav>
        <div *ngFor="let section of menu">
            <div class="nav-title">{{section.section}}</div>
        </div>
    </nav>
Enter fullscreen mode Exit fullscreen mode

Next, menu items need to be added to each section. For this particular application, each menu item will be required to have a route, name, and icon (boxicons). In making sure this theory is going to work out, the menu has been updated with the first item:

public menu: Array<any> = [
        {   section: 'Recipes', 
            items: [
                {   name: 'My Recipes', 
                    route: '/recipes',
                    icon: 'bxs-food-menu'
                }
            ]    
        },
        { section: 'Account Managment'}
    ]
Enter fullscreen mode Exit fullscreen mode

And after a bit of testing and working out the code, here is the updated template:

 <nav>
    <div *ngFor="let section of menu">
        <div class="nav-title">{{section.section}}</div>
        <ul>
            <li class="nav-item" [routerLink]="[item.route]"  *ngFor="let item of section.items">
                <i class="bx {{item.icon}}"></i>
                <span>{{item.name}}</span>
            </li>
        </ul>
    </div>
    <div class="nav-title"></div>
    <ul><li class="nav-item" (click)="logout()">
            <i class="bx bx-log-out-circle"></i>
            <span>Logout</span>
        </li>
    </ul>
 </nav>
Enter fullscreen mode Exit fullscreen mode

Angular loops over the menu to get each section, and then loops over the section to get the menu items. This is pretty much the final template code including the logout function. Technically, it could be made a route and mesh into the JSON menu, but for now it's a function within the menu component.

The controlling menu array is much easier to manage. New sections and menu items can be easily added or removed. This paradigm could be extended to include roles for portal type menus. You could also put the menu in a separate JSON file and modify outside the application thus yielding the ability to update the menu without redeploying the application. The caveat to that idea is in maintaining the routes because they are not generally programmatically loaded (although that could just be my inexperience showing).

public menu: Array<any> = [
    {   section: 'Recipes', 
        items: [
            {   name: 'My Recipes', 
                route: '/recipes',
                icon: 'bxs-food-menu'
            },
            {   name: 'New Recipe',
                route: '/edit-recipe/new',
                icon: 'bxs-plus-circle'
            },
            {   name: 'Grocery Lists',
                route: '/grocerylists',
                icon: 'bxs-shopping-bag'
            },
            {   name: 'Search',
                route: '/search',
                icon: 'bxs-search'
            },
            {   name: 'Help',
                route: '/help',
                icon: 'bxs-help-circle'
            }
        ]    
    },
    {   section: 'Account Managment',
        items: [
            {   name: 'Profile',
                route: '/profile',
                icon: 'bxs-user'
            }, 
            {   name: 'Support Us',
                route: '/support',
                icon: 'bx-home-heart'
            }, 
            {   name: 'Contact Us',
                route: '/contact',
                icon: 'bx-support'
            }
        ]
    }
]
Enter fullscreen mode Exit fullscreen mode

The final result:

Image of each item in the menu

Hope you found this interesting and easy to follow. I will never profess to be a best practice kind of developer. My goal is to get things working as quickly as possible with an eye toward enhancements, maintenance, and readability. Good or otherwise, I'd love to hear your feedback as that helps me become a better developer.

Cheers!

Top comments (0)