How to handle switch cases in Meteor templates

handlebars.js, meteor, templates

Solution

Not sure what you’re trying to output, but a helper that evaluates equality will likely be involved. Put this in one of your project’s client-loaded JavaScript files:

Template.registerHelper("equals", function (a, b) {
  return (a == b);
});

Then you can create something similar to a switch-case structure using `if` blocks and your new `equals` helper. For example, if you're storing the state of a button in a variable named `btnState` and the possible values are `1`, `2` or `3`:

<button class="{{#if equals btnState 1}}btn-active{{/if}}
               {{#if equals btnState 2}}btn-inactive{{/if}}
               {{#if equals btnState 3}}btn-disabled
               {{else}}btn-default{{/if}}">

If you want `switch`-`case`'s ability to short-circuit further tests after it hits a true value, and/or a default case at the end, the way to do so is with an ugly set of nested `if`-`else` blocks:

<button class="{{#if equals btnState 1}}
                 btn-active
               {{else}}
                 {{#if equals btnState 2}}
                   btn-inactive
                 {{else}}
                   {{#if equals btnState 3}}
                     btn-disabled
                   {{else}}
                     btn-default
                   {{/if}}
                 {{/if}}
               {{/if}}">

This example is almost trivially simple; I’m assuming that you’re using this as a control structure for a large template, where instead of something like `btn-default` you have dozens of lines of HTML.

If you really are using it for short snippets of text like CSS class names, you could instead create a helper that maps a set of cases with a set of strings to be returned. For example (CoffeeScript):

Template.registerHelper "switch", (input, cases, output, def) ->
  # input is the variable we're comparing, i.e. switch(input)
  # cases is an EJSON-stringified array, i.e. case "foo", case "bar"
  # output is an EJSON-stringified array of strings to return for each case
  # def (default) is a string to return if none of the cases are met

  # Validate input, convert EJSON strings into arrays:
  unless input? and _.isString(cases) and _.isString(output)
    return ""
  cases = EJSON.parse cases
  output = EJSON.parse output
  unless _.isArray(cases) and _.isArray(output) and 
    cases.length is output.length
      return ""

  # Evaluate each case, returning as soon as the first case is true:
  for value, index in cases
    return output[index] if input is value

  # If we've made it this far, none of the cases were met; return def (default):
  if def? and _.isString(def) then return def else return ""

And to use it:

{{switch btnState "[1,2,3]"
  "[\"btn-active\",\"btn-inactive\",\"btn-disabled\"]" "btn-default"}}

Handlebars doesn’t allow passing arrays or objects into helpers, hence the contortions with JSON strings passed as parameters and then parsed.

Problem

What's the best way to handle a switch case in a template (eg: a button with 4 possible states)? It seems wrong to pull the html out into a helper, but it feels just as bad to put the logic into the template... Edit: As you can see, putting the html in a helper is far from ideal, but changing a class, a tag-name, properties, and content, would make the template wholly unreadable. ``` Template.nextMeetup.helpers({ rsvpButton: function(rsvp) { var button; switch(rsvp){ case 'yes': button = '<a ' + this.event_url + 'class="rsvp btn btn-success pull-right" title="visit event page" target="_blank"><i class="icon-check"></i> I\'m Attending</a>'; break; case 'maybe': button = '<a ' + this.event_url + 'class="rsvp btn btn-warning pull-right" title="visit event page" target="_blank"><i class="icon-warning-sign"></i> I might go</a>'; break; case 'no': button = '<a ' + this.event_url + 'class="rsvp btn btn-danger pull-right" title="visit event page" target="_blank"><i class="icon-remove"></i> I\'m not going</a>'; break; case 'none': button = '<a ' + this.event_url + 'class="rsvp btn btn-inverse pull-right" title="visit event page" target="_blank"><i class="icon-spinner"></i> I havn\'t decided</a>'; break; default: button = '<button class="rsvp signIn btn btn-disabled pull-right">Sign in to RSVP</button>'; } return new Handlebars.SafeString(button); } }); ```

Original source