How to make an accordion with ReactiveVar


#1

Hi I’m having an issue with my accordion component in Meteor, Here is my code:

Meteor 1.4

Template.accordion.onCreated(function() {
  this.accordionOpen = new ReactiveVar(true);
});

Template.accordion.events({
  'click .accordionOpen-js'(event, template) {
    template.accordionOpen.set(false);
  },
  'click .accordionClose-js'(event, template) {
    template.accordionOpen.set(true);
  }
});

<template name="filters">
  <div class="fu-stores-filter">
    <div class="mdl-grid">
      <div class="mdl-cell mdl-cell--12-col filterclass">
        <div class="fu-filter-menu filterclass">
          <div class="fu-filter-menu__heading">
            <div class="fu-filter-menu__heading--title fu-filter-menu__heading--link fu-show-all-categories-js"> All </div>
          </div>
          <div class="fu-filter-menu__heading">
            <div class="fu-filter-menu__heading--title"> Adults </div>
          </div>
          <div class="fu-filter-menu__heading {{#if accordionOpen}}accordionOpen-js{{else}}accordionClose-js{{/if}}">
            <h4 class="fu-filter-menu__heading--title">
              Men
              <button class="fu-accordion-button mdl-button mdl-js-button mdl-button--icon">
                <i class="fu-accordion-icon fu-arrow-up"></i>
              </button>
             </h4> </div>
          <div class="fu-filter-menu__body {{#unless accordionOpen}}hidden{{/unless}}">
            <ul class="mdl-list"> {{#each categoriesMen}}
              <li class="mdl-list__item-primary-content men-js" id='{{alias}}'>{{alias}}</li>
              {{/each}}
            </ul>
          </div>
          <div class="fu-filter-menu__heading {{#if accordionOpen}}accordionOpen-js{{else}}accordionClose-js{{/if}}">
            <h4 class="fu-filter-menu__heading--title">
              women
              <button class="fu-accordion-button mdl-button mdl-js-button mdl-button--icon">
                <i class="fu-accordion-icon fu-arrow-up"></i>
              </button>
            </h4> </div>
          <div class="fu-filter-menu__body {{#unless accordionOpen}}hidden{{/unless}}">
            <ul class="mdl-list"> {{#each categoriesWomen}}
              <li class="mdl-list__item-primary-content women-js" id='{{alias}}'>{{alias}}</li>
              {{/each}}
            </ul>
          </div>
          <div class="fu-filter-menu__heading {{#if accordionOpen}}accordionOpen-js{{else}}accordionClose-js{{/if}}">
            <h4 class="fu-filter-menu__heading--title">
              Unisex
            </h4> </div>
          <div class="fu-filter-menu__body {{#unless accordionOpen}}hidden{{/unless}}">
            <ul class="mdl-list"> {{#each categoriesUnisex}}
              <li class="mdl-list__item-primary-content unisex-js" id='{{alias}}'>{{alias}}</li>
              {{/each}}
            </ul>
          </div>
        </div>
      </div>
</template>

The problem is that when I click to one of the taps all the taps open and all the taps close. Is there a way to bind each tap?


#2

You could try disabling event bubbling by returning false:

Template.accordion.events({
  'click .accordionOpen-js'(event, template) {
    template.accordionOpen.set(false);
    return false;
  },
  'click .accordionClose-js'(event, template) {
    template.accordionOpen.set(true);
    return false;
  }
});

Read http://docs.meteor.com/api/templates.html#eventmaps

Most events bubble up the document tree from their originating element…

Returning false from a handler is the same as calling both stopImmediatePropagation and preventDefault on the event.


#3

It’s because you’re using the same variable accordionOpen for each tab, so if accordionOpen is true, all tabs will open and vice versa. You need to give each tab some identifier, set the ReactiveVar to that identifier value when the tab is clicked, then use a template conditional that compares the ReactiveVar value to the tab identifier value to decide whether to hide or show the tab.


#4

Hi @babrahams can you please provide me an example? :slight_smile: cuz I think that is the actual problem.


#5

hi @robfallows,

I’ve added the return false but still the same problem :frowning:


#6

No worries - it was a long shot, but at least is really quick to test :slight_smile:


#7

So here is how one of my college fix the problem:

First a separate template with a specific parameter that will hold each one of the tabs of the accordion:

filterTab.html

<template name="filterTab">
  <div class="fu-filter-menu__heading accordion-js">
    <h4 class="fu-filter-menu__heading--title">
      {{title}}
      <button class="fu-accordion-button mdl-button mdl-js-button mdl-button--icon">
        <i class="material-icons">{{#if closed}}keyboard_arrow_down{{else}}keyboard_arrow_up{{/if}}</i>
      </button>
    </h4>
  </div>
  <div class="fu-filter-menu__body {{#if closed}}close{{/if}}">
    {{> Template.contentBlock}}
  </div>
</template>


Then the JS file for the event handlers and ReactiveVar

filterTab.js

import {Template} from 'meteor/templating';
import {ReactiveVar} from 'meteor/reactive-var';

import './filter_tab.html';

Template.filterTab.onCreated(function() {
  this.closed = new ReactiveVar(false);
});

Template.filterTab.helpers({
  closed() {
    return Template.instance().closed.get();
  }
});

Template.filterTab.events({
  'click .accordion-js'(event, template) {
    const closed = template.closed;

    closed.set(!closed.get());
  }
});

Finally we added the filterTab template as a helper for each one of the possible accordions.

filter.html

                  {{#if categoriesMen.count}}
                    {{#filterTab title='Men'}}
                      <ul class="mdl-list">
                        {{#each categoriesMen}}
                          <li class="mdl-list__item-primary-content men-js" id='{{alias}}'>{{alias}}</li>
                        {{/each}}
                      </ul>
                    {{/filterTab}}
                  {{/if}}
                  {{#if categoriesWomen.count}}
                    {{#filterTab title='women'}}
                      <ul class="mdl-list">
                        {{#each categoriesWomen}}
                          <li class="mdl-list__item-primary-content women-js" id='{{alias}}'>{{alias}}</li>
                        {{/each}}
                      </ul>
                    {{/filterTab}}
                  {{/if}}
                  {{#if categoriesUnisex.count}}
                    {{#filterTab title='Unisex'}}
                      <ul class="mdl-list">
                        {{#each categoriesUnisex}}
                          <li class="mdl-list__item-primary-content unisex-js" id='{{alias}}'>{{alias}}</li>
                        {{/each}}
                      </ul>
                    {{/filterTab}}
                  {{/if}}

and voila it works like magic and it is very clean :wink: