Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle switch cases in Meteor templates

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);
    }
});
like image 706
Sinetheta Avatar asked Jul 14 '13 21:07

Sinetheta


People also ask

How do I use templates in Meteor?

Within Meteor, templates are used to create a connection between the project’s interface and the project’s JavaScript code. When we place interface elements inside a template, such as a button or a form, we’re able to reference those elements and build an interface that users can interact with.

How do I call a method from the client in Meteor?

This Method is callable from the client and server using Meteor.call. Note that you should only use a Method in the case where some code needs to be callable from the client; if you just want to modularize code that is only going to be called from the server, use a regular JavaScript function, not a Method.

How do you handle errors in Meteor methods?

Error handling. In regular JavaScript functions, you indicate errors by throwing an Error object. Throwing errors from Meteor Methods works almost the same way, but a bit of complexity is introduced by the fact that in some cases the error object will be sent over a websocket back to the client.

What are methods in Meteor?

Methods are Meteor’s remote procedure call (RPC) system, used to save user input events and data that come from the client. If you’re familiar with REST APIs or HTTP, you can think of them like POST requests to your server, but with many nice features optimized for building a modern web application.


2 Answers

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.

like image 56
Geoffrey Booth Avatar answered Oct 03 '22 22:10

Geoffrey Booth


Use one template per button and an additional dynamic template

Your template helper, returning the dynamic template:

Template.nextMeetup.helpers({
    rsvpButtonTemplate: function(rsvp) {
        switch(rsvp){
            case 'yes':   return Template.buttonYes;
            case 'maybe': return Template.buttonMaybe;
            case 'no':    return Template.buttonNo;
            case 'none':  return Template.buttonNone;
        }
    }
});

Your main template, calling the template helper:

<template name="myPage">
    ...
    {{> rsvpButtonTemplate}}
    ...
</template>

Your 4 buttons:

<template name="buttonYes">
    <a {{event_url}} class="rsvp btn btn-success pull-right" title="visit event page" target="_blank"><i class="icon-check"></i> I'm Attending</a>
</template>
<template name="buttonMaybe">
    <a {{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>
</template>
<template name="buttonNo">
    <a {{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>
</template>
<template name="buttonNone">
    <a {{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>
</template>
like image 20
steph643 Avatar answered Oct 03 '22 21:10

steph643