With a Handlebars.js template like this...
I just had a similar need as the OP--with a static set of select options, but a dynamic selected value. I really like @janjarfalk's solution, but I'm using node.js and don't have jQuery pulled in. So, I put together my own variation based on RegExp's. Hope this is helpful to others.
Handlebars helper:
hbs.registerHelper('select', function(selected, options) {
return options.fn(this).replace(
new RegExp(' value=\"' + selected + '\"'),
'$& selected="selected"');
});
Handlebars template:
<select>
{{#select CurrentSort}}
<option value="1">Most Recent First</option>
<option value="2">Most Recent Last</option>
<option value="3">Highest Score First</option>
<option value="4">Highest Score Last</option>
<option value="5">Most Comments</option>
<option value="6">Fewest Comments</option>
<option value="7">Most Helpful Votes</option>
<option value="8">Fewest Helpful Votes</option>
{{/select}}
</select>
You can tweak the helper to work even if you don't use the value
attribute--just adjust the regexp to search the element text, and do the string replacement before the matched text.
Improved answer of @lazd to select first option when nothing matches.
Handlebars.registerHelper('select', function(value, options) {
// Create a select element
var select = document.createElement('select');
// Populate it with the option HTML
$(select).html(options.fn(this));
//below statement doesn't work in IE9 so used the above one
//select.innerHTML = options.fn(this);
// Set the value
select.value = value;
// Find the selected node, if it exists, add the selected attribute to it
if (select.children[select.selectedIndex]) {
select.children[select.selectedIndex].setAttribute('selected', 'selected');
} else { //select first option if that exists
if (select.children[0]) {
select.children[0].setAttribute('selected', 'selected');
}
}
return select.innerHTML;
});
Usage remains same:
<select>
{{#select status}}
<option>Option 1</option>
<option>Option 2</option>
<option value="Option 3">Option 3 (extra info)</option>
<option value="Option 4">Option 4 (more extra)</option>
{{/select}}
</select>
This might take more code in the template, but it is easier to read:
.js
Handlebars.registerHelper('select', function(selected, option) {
return (selected == option) ? 'selected="selected"' : '';
});
.hbs
<select name="status">
<option value="public" {{{select story.status 'public'}}}>Public</option>
<option value="private" {{{select story.status 'private'}}}>Private</option>
<option value="unpublished" {{{select story.status 'unpublished'}}}>Unpublished</option>
</select>
I know this does not answer the question directly, but in this situation, I passed the unselected html options to the template, and after it is rendered, I use jquery to mark as selected the value that the json object indicates
I saw the extremely clever solution posted by @janjarfalk and realized it didn't work for options defined without a value attribute (such as <option>Value</option>
). My application needed that, and I wanted a helper done in vanilla JavaScript for performance, so I came up with the following.
This solution will support <option>Both a label and a value</option>
in addition to <option value="aValue">A label</option>
and will be much faster as it doesn't use jQuery.
Handlebars.registerHelper('select', function(value, options) {
// Create a select element
var select = document.createElement('select');
// Populate it with the option HTML
select.innerHTML = options.fn(this);
// Set the value
select.value = value;
// Find the selected node, if it exists, add the selected attribute to it
if (select.children[select.selectedIndex])
select.children[select.selectedIndex].setAttribute('selected', 'selected');
return select.innerHTML;
});
Usage:
<select>
{{#select status}}
<option>Option 1</option>
<option>Option 2</option>
<option value="Option 3">Option 3 (extra info)</option>
<option value="Option 4">Option 4 (more extra)</option>
{{/select}}
</select>
I just ran into this problem, here's a solution for when the options are dynamic..
Instead of creating a select helper, I created an option helper that accepts the value of the item you wish to be selected.
Handlebars.registerHelper('option', function(value) {
var selected = value.toLowerCase() === (this.toString()).toLowerCase() ? 'selected="selected"' : '';
return '<option value="' + this + '" ' + selected + '>' + this + '</option>';
});
And in my template.
{{#items}}
{{{option ../selected_value}}}
{{/items}}
Please note the ../ to access the parent's scope as it's not likely the selected_value will be inside of the items array.
Cheers.