4

So I'm trying to be efficient and clean in my Spacebars templates as I work with Meteor. But I'm stumped by the way in which checkboxes and select options are to be dealt with. Suppose I want to have a checkbox set as checked or not depending on a flag that is in a document in one of my collections. I don't appear to be able to do the following:

<input type='checkbox' id='item-{{this.item_id}}' {{#if checked}}checked{{/if}} />

When I try this, I get the following error:

A template tag of type BLOCKOPEN is not allowed here.

If I try the following options, though, they all result in the checkbox being checked even when the flag is false:

<input type='checkbox' id='item-{{this.item_id}}' checked='{{#if checked}}true{{/if}}' />
<input type='checkbox' id='item-{{this.item_id}}' checked='{{#if checked}}true{{else}}false{{/if}}' />

I have the same trouble with selected in my select options, so I end up doing something like the following to get around it, which seems verbose and error-prone:

<select id='option-{{this.item_id}}'>
    {{#if option_60}}
        <option value='60' selected>1 hour</option>
    {{else}}
         <option value='60'>1 hour</option>
    {{/if}}

    {{#if option_90}}
         <option value='90' selected>90 mins</option>
    {{else}}
        <option value='90'>90 mins</option>
    {{/if}}

    {{#if option_120}}
         <option value='120' selected>2 hours</option>
    {{else}}
         <option value='120'>2 hours</option>
    {{/if}}
</select>
tadasajon
  • 14,276
  • 29
  • 92
  • 144
  • Have you tried just a helper and no `{{#if}}` statement? `checked={{isChecked c=this.flag}}` I'm not sure if you can use open statements inside of tags anymore. -untested EDIT: checked one of my implementations and this is how I did it (Meteor 0.8.0+). Yes like the answer below ;D – below9k Sep 24 '14 at 20:11

3 Answers3

7

You can use non-block helpers for placing such arguments:

UI.registerHelper('checkedIf', function(val) {
  return val ? 'checked' : '';
});

<input type="checkbox" {{checkedIf checked}}>
Hubert OG
  • 19,314
  • 7
  • 45
  • 73
2

Here is an example of the code I use to solve this problem, this should be pretty straightforward.

JS

Template.myTemplate.helpers({
  checked:function(){
    // assumes that this.checked is the flag in your collection
    return this.checked?"checked":"";
  },
  options:function(){
    // store options in a helper to iterate over in the template
    // could even use http://momentjs.com/docs/#/durations/humanize/ in this case ?
    return [{
      value:60,
      text:"1 hour"
    },{
      value:90,
      text:"90 mins"
    },{
      value:120,
      text:"2 hours"
    }];
  },
  selected:function(value){
    // compare the current option value (this.value) with the parameter
    // the parameter is the value from the collection in this case
    return this.value==value?"selected":"";
  }
});

Template.parent.helpers({
  dataContext:function(){
    // dummy data, should come from a collection in a real application
    return {
      checked:true,
      value:90
    };
  }
});

HTML

<template name="myTemplate">
  <input type="checkbox" {{checked}}>
  <select>
    {{#each options}}
      {{! ../ syntax is used to access the parent data context which is the collection}}
      <option value="{{value}}" {{selected ../value}}>{{text}}</option>
    {{/each}}
  </select>
</template>

<template name="parent">
  {{> myTemplate dataContext}}
</template>

EDIT : using universal helpers as Hubert OG hinted at :

JS

Template.registerHelper("checkedIf",function(value){
  return value?"checked":"";
});

Template.registerHelper("selectedIfEquals",function(left,right){
  return left==right?"selected":"";
});

HTML

<template name="myTemplate">
  <input type="checkbox" {{checkedIf checked}}>
  <select>
    {{#each options}}
      <option value="{{value}}" {{selectedIfEquals value ../value}}>{{text}}</option>
    {{/each}}
  </select>  
</template>
saimeunt
  • 22,666
  • 2
  • 56
  • 61
2

The best, most efficient and effective way to accomplish this is to setup global template helpers, one each for determining checked and selected values. For documentation on creating global template helpers, see this documentation.

For checked, I suggest implementing it in this way:

Template.registerHelper('isChecked', function(someValue) {
    return someValue ? 'checked' : '';
});

For selected, I suggest implementing it in this way:

Template.registerHelper('isSelected', function(someValue) {
    return someValue ? 'selected' : '';
});

With these two global template helpers implemented, you can use them in any of your templates within your application like this:

<template name="someTemplate">
    <input type="checkbox" {{isChecked someValue}}>

    <select>
        {{#each someOptions}}
            <option {{isSelected someValue}}>{{someDisplayValue}}</option>
        {{/each}}
    </select>
</template>
Keith Dawson
  • 1,475
  • 16
  • 27