Express – highlight selected menu item

In my previous post, I wrote about Handlebars in Express basics. One of my goals was to create the menu which will highlight currently selected page. I don’t want to do this client side. I prefer to generate the proper menu on the server and skip JavaScript client side as much as possible. To achieve my goal, I had to learn few more things and write few lines of code. I’m not saying that my solution is the best – but it is working. Once I will learn that there was a better solution, I will update this article.

Main Menu partial

I started by creating a new file in /views/partials/ directory – I called it mainMenu.hbs. I copied menu related piece of my main layout to this file and included my new menu partial into layout by using:

{{> mainMenu}}

Partials are already registered so there was nothing more to do. My old menu was working as before and I was able to start implementing menu generator engine.

New module creation

I don’t want to place all my functions in the app.js file – they should be stored in modules. I’m new to the node so I also had to learn how to create one. I started by creating a new file in new directory /modules/tools.js – I want to use it to handle various general actions. I used general module layout and this was the first version:

var exports = module.exports = {};

exports.generateMainMenu = function(req, res, next) {
    var menuItems = [{
            label: 'Home',
            href: '/'
        }, {
            label: 'About',
            href: '/about'
        }
    ]
    for (var item in menuItems) {
        if (menuItems[item].href == req._parsedUrl.pathname) {
            menuItems[item].class = 'pure-menu-selected';
        }
    }
    res.locals.menuItems = menuItems;
    next();
};

As you can see, the function I created follows the general middleware scheme – it requires req, res and next variables and calls next at the end of the execution. What is this function doing? It checks each menu item against parsed URL path name. If the selected path matches given menu item, this item is highlighted by adding new CSS class. Whole menu object is saved in res.locals.menuItems – this is the best place to store variables that should be passed from one middleware to another during the whole request handling process.

Menu generated, it’s time to display it

I want to pass my new menu object to handlebars and render menu items based on these values. By default, handlebars are not having access to res locals, but I can change it. Because I already registered partials, in my app.js file I already have hbs variable created. I can now simply add this line of code after my partials registration:

hbs.localsAsTemplateData(app);

This way I gave handlebars access to app.locals and res.locals as template data. I can now use all variables stored in locals on my templates and layouts. It’s time to do some magic in the mainMenu.hbs file. I want to use res.locals.menuItems to render menu. Because locals are accessible from the template now, I used ‘each’ to list all menu items:

<ul class="pure-menu-list">
    {{#each menuItems}}   
        <li class="pure-menu-item {{ class }}">
            <a href="{{ href }}" class="pure-menu-link">
                {{ label }}
            </a>
        </li>
    {{/each}}
</ul>

As you probably remember, if there is no ‘class’ property set for given menu item, handlebars engine is replacing this variable an with empty string. This way I created simple, single level menu. I guess that multi-level menu will be a little bit more complicated and I will try to implement such thing in the future.

You can find this project source on GitHub.

Leave a Reply

Your email address will not be published. Required fields are marked *