Reputation: 2171
With a Handlebars.js template like this...
<select>
<option value="Completed">Completed</option>
<option value="OverDue">OverDue</option>
<option value="SentToPayer">SentToPayer</option>
<option value="None">None</option>
</select>
... and data like this...
{
"id" : 1,
"name" : "World"
"status" : "OverDue",
"date" : "2012-12-21"
}
I want to render HTML like this.
<select>
<option value="Completed">Completed</option>
<option value="OverDue" selected="selected">OverDue</option>
<option value="SentToPayer">SentToPayer</option>
<option value="None">None</option>
</select>
Which way is the easiest?
Upvotes: 56
Views: 61182
Reputation: 869
for arrays
function select(selected, options) {
return options.fn(this).replace( new RegExp(' value=\"' + selected + '\"'), '$& selected="selected"').replace( new RegExp('>' + selected + '</option>'), ' selected="selected"$&');
},
Upvotes: 0
Reputation: 23
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
Upvotes: 0
Reputation: 297
Data source
selectedValue: "8",
option:[
{id_sub_proyectop: "8", clave: "021", name: "Cliclismo"},
{id_sub_proyectop: "9", clave: "022", name: "Atletismo"},
],
helper
Handlebars.registerHelper('selected', function(value, prop) {
if (value === undefined){ return '';};
return value === this[prop] ? 'selected="selected"' : '';
});
Template
<div class="medium-6 cell">
<label>Sub Proyecto / Proceso:
<select name="id_sub_proyectop" required>
{{#each option}}
<option value="{{id_sub_proyectop}}" {{{selected ../selectedValue 'id_sub_proyectop'}}}>{{clave}} - {{name}}</option>
{{/each}}
</select>
</label>
</div>
Upvotes: 1
Reputation: 431
Today I was also facing the same problem I'm creating a Content Management System and to fetch the status of the post I stuck and in searching for a solution, I landed on this page I found a few answers relevant Because I'm using server-side data and when I used document.createElement it is throwing error document is not defined.
The Regex solution worked for me but I want an easy to understand one whether it is verbose so I came with this solution.
Handlebars.registerHelper('getValue', function(value, options) {
if(options.fn(this).indexOf(value) >= 1){
return `selected='selected'`;
}
});
in the template use the code in this way
<select name="post-status" id="post-status">
<option {{#getValue posts.postStatus}} value="Draft" {{/getValue}} >Draft</option>
<option {{#getValue posts.postStatus}} value="private" {{/getValue}} >private</option>
<option {{#getValue posts.postStatus}} value="publish" {{/getValue}} >publish</option>
</select>
If I'm wrong somewhere Please correct me.
Upvotes: 1
Reputation: 1
Handlebars.registerHelper('select', function( value, options ){
return options.fn(this)
.replace( new RegExp(' value=\"' + value + '\"'), '$& selected="selected"')
.replace( new RegExp('>' + value + '</option>'), ' selected="selected"$&');
});
user.country from db session
country stored in country.json file
<select id="country" name="country" class="form-control">
<option value="" selected="selected">(please select a country)</option>
{{#select user.country}}
{{#each countries as |value key| }}
<option value="{{ value.code }}">{{ value.name }}</option>
{{/each}}
{{/select}}
</select>
Upvotes: 0
Reputation: 707
Another one solution using express-handlebars
and dynamic options is this.
Helper function (From all options takes the one we want and change it to selected).
select: function(selected, options) {
return options.fn(this)
.replace( new RegExp(' value=\"' + selected + '\"'), '$& selected="selected"')
.replace( new RegExp('>' + selected + '</option>'), ' selected="selected"$&');
}
handlebars file (I just use #each inside select to receive me data and worked like a charm).
<select name="justAname">
{{#select content.campusId}}
{{#each campus}}
<option value="{{id}}">{{name}}</option>
{{/each}}
{{/select}}
</select>
Upvotes: 0
Reputation: 1429
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>
Upvotes: 4
Reputation: 171
Works for me
<select>
<option value="{{status}}" hidden="hidden" selected="selected">{{status}}</option>
<option value="Completed">Completed</option>
<option value="OverDue">OverDue</option>
<option value="SentToPayer">SentToPayer</option>
<option value="None">None</option>
</select>
Upvotes: 17
Reputation: 9091
@lazd's answer does not work for <option>
elements within an <optgroup>
.
selectedIndex
is numbered monotonically for all <option>
s, but select.children
holds the <optgroup>
s, and select.children[n].children
holds the <option>
s within <optgroup>
n (with numbering restarting within each <optgroup>
, of course).
This alternative version will work for <option>
elements within <optgroup>
s:
Handlebars.registerHelper('select-optgrp', function(value, options) {
var select = document.createElement('select'); // create a select element
select.innerHTML = options.fn(this); // populate it with the option HTML
select.value = value; // set the value
var g = 0, i = select.selectedIndex; // calculate which index of which optgroup
while (i >= select.children[g].children.length) { i -= select.children[g].children.length; g++; }
if (select.children[g].children[i]) { // if selected node exists add 'selected' attribute
select.children[g].children[i].setAttribute('selected', true);
}
return select.innerHTML;
});
Upvotes: 0
Reputation: 3883
It should be mentioned that if you do not care about repeats... you can just use vanilla handlebars and place the selected option first, such as:
<select name="ingredient">
<option value="{{ingredient.id}}" selected>{{ingredient.name}}</option>
{{#each ingredients}}
<option value="{{this.id}}">{{this.name}}</option>
{{/each}}
</select>
Upvotes: 0
Reputation: 3228
If you have very few options and you don't want to write a helper, here is what you can do:
//app.js
var data = {selectedVal: 'b'};
// can also use switch ... case ...
if (data.selectedVal === 'a') {
data.optionASelected = true;
} else if (data.selectedVal === 'b') {
data.optionBSelected = true;
}
// assuming you have a template function to handle this data
templateFunc(data);
In your template file:
<!-- template.html -->
<select id="select-wo-helper" >
<option value="a" {{#if optionASelected}} selected {{/if}}>A</option>
<option value="b" {{#if optionBSelected}} selected {{/if}}>B</option>
</select>
Again this may NOT be the best solution of all, but it probably is a very quick work around when you are dealing very few options and wanted a quick fix.
Upvotes: 2
Reputation: 33
I prefer to use a template approach. By this I mean the layout of the option tag itself is specified in the handlebars template (where someone might look for it) and not in the javascript helper. Template inside the block helper is passed into the helper script and can be used by calling options.fn() which then uses any script changes you have made in your helper.
Template:
<select>
{{#optionsList aStatusList sCurrentStatusCode 'statusCode'}}
<option {{isSelected}} value="{{statusCode}}">{{statusName}}</option>
{{/optionsList}}
</select>
Slightly modified data (not required but a little more "real world" for me)
var myOrder =
{
"id" : 1,
"name" : "World",
"statusName" : "OverDue", /* status should NOT be here! */
"statusCode" : "1",
"date" : "2012-12-21"
}
var sCurrentStatusCode = myOrder.statusCode;
var aStatusList =
[
{
"statusName" : "Node",
"statusCode" : 0
},
{
"statusName" : "Overdue",
"statusCode" : 1
},
{
"statusName" : "Completed",
"statusCode" : 2
},
{
"statusName" : "Sent to Payer",
"statusCode" : 3
}
]
Javascript registered helper:
Handlebars.registerHelper( 'optionsList',
function ( aOptions, sSelectedOptionValue, sOptionProperty, options )
{
var out = "";
for ( var i = 0, l = aOptions.length; i < l; i++ )
{
aOptions[ i ].isSelected = '';
if( ( sOptionProperty != null && sSelectedOptionValue == aOptions[ i ][ sOptionProperty ] ) || ( sSelectedOptionValue == aOptions[ i ] ) )
{
aOptions[ i ].isSelected = ' selected="selected" ';
}
out = out + options.fn( aOptions[ i ] );
}
return out;
} );
optionsList is what I have chosen to name this helper
aStatusList an array of status objects contain several properties including the status value/name (in most cases I have encountered this would be the status code not the status name that is stored )
sCurrentStatus is the previously selected status code (not the value) and is the option value that i would like to have the selected in this generated option list.
statusCode is the string property name within a aStatusList object that I will test to see if it matches myStatus that is aStutusList[ loopIndex ][statusCode]
Upvotes: 3
Reputation: 2171
I found a lot of over complicated solutions and decided to write my own using a Handlebars helper.
With this partial (using Jquery) ...
window.Handlebars.registerHelper('select', function( value, options ){
var $el = $('<select />').html( options.fn(this) );
$el.find('[value="' + value + '"]').attr({'selected':'selected'});
return $el.html();
});
You can wrap selects in your Handlebars template with {{#select status}}...
<select>
{{#select status}}
<option value="Completed">Completed</option>
<option value="OverDue">OverDue</option>
<option value="SentToPayer">SentToPayer</option>
<option value="None">None</option>
{{/select}}
</select>
and end up with this...
<select>
<option value="Completed">Completed</option>
<option value="OverDue" selected="selected">OverDue</option>
<option value="SentToPayer">SentToPayer</option>
<option value="None">None</option>
</select>
Presto!
Upvotes: 95
Reputation: 444
I've had problems with the "select block" approach when using the "each" helper to build something dynamic, due to the context.
Here is my solution:
Handlebars.registerHelper('option', function(value, label, selectedValue) {
var selectedProperty = value == selectedValue ? 'selected="selected"' : '';
return new Handlebars.SafeString('<option value="' + value + '"' + selectedProperty + '>' + label + "</option>");
});
And the template:
<select>
{{#each status}}
{{option id name ../statusId}}
{{/each}}
</select>
Upvotes: 11
Reputation: 10863
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>
Upvotes: 7
Reputation: 1333
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.
Upvotes: 40
Reputation: 31
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.
Upvotes: 2
Reputation: 4687
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>
Upvotes: 20