Dropdown menus in Meteor

I’ve been developing a site in Meteor, using their default templating engine Blaze.

And here’s a question: what’s the Meteor-like way to show a dropdown menu?

I’m using Bootstrap, so of course we can use Bootstrap’s javascript (based on jQuery) to do this. But it doesn’t feel quite right – shouldn’t we use a reactive approach, eg. with a reactive Session variable telling us whether the dropdown is open?

Surprisingly, I haven’t seen any such examples in my Google travels.

So until I get my act together to publish this as a Meteor package, here is a way to do it.

Define this html template:

<template name="dropdown">
    {{> Template.dynamic template=templateName data=templateData}}
</template>

And put this javascript in your client folder. Note it uses underscore for _.extend, but you should be able to remove this dependency if you need to.

//
// if the ESC key is pressed or a mouse is clicked anywhere, close any open dropdowns
//

$(document).keyup(function(evt) {
    if (evt.keyCode === 27) {
        Session.set('dropdown', null);
    }
});

// You will need to change the word "layout" here to be a template
// that you define which covers your entire webpage
Template.layout.events({
    'click': function() {
        Session.set('dropdown', null);
    }
});

Template.dropdown.helpers({

    "templateName": function() {
        return 'dropdown_' + this.name;
    },

    "templateData": function() {
        // add an 'open' property to the template for the child to tell if it is open
        // _.extend(dest, src) copies all the properties in src to dest and returns dest
        return _.extend({open: Session.equals('dropdown', this.name)}, this);
    }

});

Template.dropdown.events({

    'click button': function(evt) {
        evt.preventDefault();
        evt.stopPropagation();  // stops the full-page click handler above from firing
        Session.set('dropdown', this.name);
    },

    'click .dropdown-menu li a': function(evt) {
        evt.preventDefault();
        Session.set('dropdown', null);
    }

});

Note that I’ve defined a click handler which fires on any click anywhere on the webpage (you need to change the line Template.layout.events to your own page-wide template), and which closes any open dropdowns. I then use evt.stopPropagation() to stop that firing when you click the button which opens the dropdown (otherwise it could never open). If you define any other click events which should not affect open dropdowns, then you need to include evt.stopPropagation() in their definition.

With that done, it’s easy to put a dropdown in your template. Just include:

{{>dropdown name='example'}}

This finds the template dropdown_example, which will have access to an “open” method to tell if it is open. So you can define it like this:

<template name="dropdown_example">
    {{#if open}}
        <ul class="dropdown-menu x">
            <li><a href="#">Option 1</a></li>
            <li><a href="#">Option 2</a></li>
            <li><a href="#">Option 3</a></li>
        </ul>
        <button class="invisible btn">Try this</button>
    {{else}}
        <button class="btn">Try this</button>
    {{/if}}
</template>

If you need to pass the dropdown menu template some data, you can pass it directly:

{{>dropdown name='example' someData='foo'}}

and then dropdown_example will have access to the variable someData.

Hope that helps! If you have a better solution, or find any mistakes, please let me know.

  

One thought on “Dropdown menus in Meteor”

  1. Thank you for this post! I had a problem with the hyperlinks within within the dropdown menu. As soon as links are clicked, the whole menu deactivates because it’s triggering the click event for the whole page. Any suggestions on how to fix this please?

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>