Set value of a select HTML element reactively with Meteor - meteor

I want to set the value of an HTML <select> element reactively, without changing the various options for the element. I have found a solution, but it in not elegant.
To test, create a barebones Meteor app with create meteor select and change the contents of the select.html and select.js files to the following:
select.html
<body>
{{> select}}
</body>
<template name="select">
<label for="select">{{category}}</label>
<select id="select">
<option value='' disabled selected style='display:none;'>Select...</option>
<option value="Animal">Animal</option>
<option value="Vegetable">Vegetable</option>
<option value="Mineral">Mineral</option>
</select>
</template>
select.js
if (Meteor.isClient) {
Session.set("category", "")
Session.set("label", "Category")
Template.select.onRendered(function () {
setSelectValue()
})
Template.select.helpers({
category: function () {
setSelectValue()
return Session.get("label")
}
});
function setSelectValue() {
var select = $("#select")[0]
if (select) {
select.value = Session.get("category")
}
}
}
Now launch your app. In the browser console, you can change the value of the category Session variable:
Session.set("category", "Animal")
However, the select element will not update until you change the label:
Session.set("label", "category") // was "Category'
Now the select element updates, and any subsequent change the category Session variable will also update the select element.
Session.set("category", "Vegetable") // Now the select element updates
Is it possible to achieve the same effect directly, without using this clumsy workaround?

Yes. You can do it as follows:
<select id="select">
<option value="Animal" {{animalSelected}}>Animal</option>
<option value="Vegetable" {{vegetableSelected}}>Vegetable</option>
<option value="Mineral" {{mineralSelected}}>Mineral</option>
</select>
And helpers that looks something like this:
Template.select.helpers({
animalSelected: function () {
return (someCondition === true) ? 'selected' : '';
},
vegetableSelected: function () {
return (someOtherCondition) ? 'selected' : '';
}
});
A better way might be something like this though:
<select id="select">
{{#each options}}
<option value="{{value}}" {{selected}}>{{label}}</option>
{{/each}}
</select>
And then you can use this in the helpers to decide what is selected and what isn't.
Another option is just using standard jQuery to change the select box. Something like this:
$('[name=options]').val( 3 );
Or this SO answer.

If you want the selection set dynamically I usually use a handlebar helper like this
Template.newtask.helpers({
filler: function(element){
return Session.get(element);
}
});
then in html
<select class="browser-default" id="selectedService" name="jobtype">
<option id="zero" value="{{filler 'type'}}">{{filler 'type'}}</option>

Related

Meteor Dropdown #each over a collection

I've been around this all day and i can't figure why my each cycle is not working. I'm trying to create a dropdown with some countries.
Helper
Template.register.helpers({
countries: function(){
return Country.find({ });
},
});
View, template register
<select id="country-select">
<option disabled="disabled" selected="selected">Please Select</option>
{{#each countries}}
<option value={{ name }}>{{ name }}</option>
{{/each}}
</select>
I have records in the country collection
meteor:PRIMARY> db.country.find({ }).count() ->
4
The only options that the dropwdown displays is the placeholder.
I'm using mongol this is a country record
Try this...
<option disabled selected>Please select</option>
{{#each countries}}
<option>{{name}}</option>
{{/each}}
It works here
Try
return Country.find().fetch()
in the helper
Thanks
Hope it helps
"The solution that worked for me is by calling the 'material_select' function after the options data has been loaded.
Template.[name].rendered = function() { this.autorun(function() { var optionsCursor = OptionsList.find().count(); if(optionsCursor > 0){ $('select').material_select(); } }); };"
from https://github.com/Dogfalo/materialize/issues/1469

How do we populate a dropdown list's element from a variable in a collection in Meteor?

I am actually trying to do 2 things here:
With 2 dropdown lists,
a) the first would populate with the products, while
b) the second would populate with the colors according to the first dropdown selected.
I starts off insert a document into the collection as below:
ProductList.insert ({ product: "apple", color: "\"red\",\"green\""});
ProductList.insert ({ product: "banana", color: "\"yellow\",\"green\""});
And this is what I have so far:
Main.html
<template name="prodlist">
<select id="category-select">
<option disabled="disabled" selected="selected">Please Select</option>
{{#each prodlist}}
<option value="{{this}}">{{this}}</option>
{{/each}}
</select>
</template>
<template name="colorlist">
<select id="category-select">
<option disabled="disabled" selected="selected">Please Select</option>
{{#each colorlist}}
<option value="{{this}}">{{this}}</option>
{{/each}}
</select>
</template>
Main.js
Template.prodlist.helpers({
prodlist: function(){
return ProductList.find().map(function(doc) {
return doc.product
})
}
});
Template.colorlist.helpers({
colorlist: function(){
return ProductList.find().map(function(doc) {
return doc.color.split(",") //<-- edited with MrE's answer
})
The reason I insert color: "\"red\",\"green\"" is trying to simulate when you populate the dropdown as hardcode: return ["red", "green"], but it return as one element with the string "red","green". Is there a better approach? Also I don't know if there are packages exists that handle dropdown. Thanks in advance.
why do you try to code a string into an array instead of using an array? I'm not sure what that gets you.
If you 'really' want to use a string, you need to parse it ( with doc.color.split(",") ) into an array instead of doing [doc.color]

Handlebars how to get sibling value inside an object

ok so iv got an object which looks like this:
Magnetim.photos = {
facebook:{albums: {id:["id1","id2","id3","id4"] , albumName:["n1","n2","n3","n4"]}},
}
inside a handlebars template im doing this line of code:
<select id="albums-selection">
{{#each photos.facebook.albums.albumName}}
<option id="{{photos.facebook.albums.id}}" value="{{this}}">{{this}}</option>
{{/each}}
</select>
this outputs this result:
<option id="id1,id2,id3,id4" value="n1">n1</option>
<option id="id1,id2,id3,id4" value="n2">n2</option>
<option id="id1,id2,id3,id4" value="n4">n3</option>
<option id="id1,id2,id3,id4" value="n4">n4</option>
but what i'm trying to achieve is for every name set the id element to its own id, so it looks like this:
<option id="id1" value="n1">n1</option>
<option id="id2" value="n2">n2</option>
etc..
While you are in the {{#each}} statement handlebars provides you with #index variable to track the current item index. Knowing this, you should be able to get corresponding item from other array.
So first you need to step outside of your {{#each}}, this is done by using ../, which will move you one level up.
My initial aproach was simply to do something like this
{{../albumName.[#index]}}
But for some reason that didn't work out. So I wrote the helper to simply grab item from array by index you pass there:
Handlebars.registerHelper('getAlbumName', function(array, id) {
return array[id];
});
Now your handlebars template will look like this:
<script id="template">
<select id="albums-selection">
{{#each id}}
<option id="{{this}}" value="{{getAlbumName ../albumName #index}}">
{{getAlbumName ../albumName #index}}
</option>
{{/each}}
</select>
</script>
<div id="output"></div>
And here is the js:
var Magnetim = {};
Magnetim.photos = {
facebook: {
albums: {
id: ["id1", "id2", "id3", "id4"],
albumName: ["n1", "n2", "n3", "n4"]
}
}
};
var source = $("#template").html();
var template = Handlebars.compile(source);
Handlebars.registerHelper('getAlbumName', function(albums, id) {
return albums[id];
});
$("#output").html(template(Magnetim.photos.facebook.albums));
Final jsfiddle with everything in place

How do I indicate 'checked' or 'selected' state for input controls in Meteor (with spacebars templates)?

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>
You can use non-block helpers for placing such arguments:
UI.registerHelper('checkedIf', function(val) {
return val ? 'checked' : '';
});
<input type="checkbox" {{checkedIf checked}}>
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>
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>

Setting the selected value of a Select element in Handlebars

I have a handlebars template that I am embedding in my html page. There is a select element with all of the available options already rendered. How can I set the selected value of the select list when I render my template?
<script id="my-template" type="text/x-handlebars-template">
<div id="my-modal">
<form action="/TestAction" method="post">
<input id="MyId" name="MyId" type="hidden" value="{{MyId}}" />
<label for="Test">Test: (optional)</label>
<select id="Test" name="Test">
<option value="">-- Choose Test --</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
</form>
</div>
</script>
If you don't want to build out the option as part of the helper, you can use a combination of a small custom handlebars helper and parent context, denoted using the ../ syntax:
First write the helper:
Handlebars.registerHelper('selected', function(option, value){
if (option === value) {
return ' selected';
} else {
return ''
}
});
And then in your template you call your helper and pass in the parent context inside the each loop to check for selectedState
{{#each states}}
<option value="{{this}}" {{selected this ../selectedState}}>{{this}}</option>
{{/each}}
I took the {{#each}} helper as inspiration, and wrote a custom helper that does the same thing: loops over a list, and continually appends the contents of the arguments to an output string. (From here: Handlebars block helpers)
In this case, I'm not passing in a chunk of template HTML to serve as the function's options parameter, like you would when using {{#each}}. Instead I am just building up the <option>
tags by brute force, and testing every iteration of the context list. Lastly, I return a big long string of <option></option> tags, and hopefully one of them has selected="selected".
The function:
Handlebars.registerHelper('options_selected', function(context, test) {
var ret = '';
for (var i = 0, len = context.length; i < len; i++) {
var option = '<option value="' + context[i]+'"';
if (test.toLowerCase() == context[i].toLowerCase()) {
option += ' selected="selected"';
}
option += '>'+ Handlebars.Utils.escapeExpression(context[i]) + '</option>';
ret += option;
}
return new Handlebars.SafeString(ret);
});
The tag in use:
<script id="form-state" type="text/x-handlebars-template">
<select name="state">
{{{options_selected states selectedState}}}
</select>
</script>
Please suggest any edits that would improve this, thanks!
You can use values straight in the Handlebars template like so.
Handlebars Template
<select id="distance">
<option value="15" {{#if (isEqual 15 distance)}} selected {{/if}}>15</option>
<option value="25" {{#if (isEqual 25 distance)}} selected {{/if}}>25</option>
<option value="50" {{#if (isEqual 50 distance)}} selected {{/if}}>50</option>
<option value="100" {{#if (isEqual 100 distance)}} selected {{/if}}>100</option>
<option value="300" {{#if (isEqual 300 distance)}} selected {{/if}}>300</option>
</select>
Handlebars Helper
define(['hbs/handlebars'], function (Handlebars) {
'use strict';
Handlebars.registerHelper('isEqual', function (expectedValue, value) {
return value === expectedValue;
});
});
Here is a solution (built over EmberJS to ease the JS part)
I refactored your sample a little, to have objects for proposed values, which can by the way carry the selected attribute...
The template part:
<script type="text/x-handlebars" data-template-name="my-template">
<div id="my-modal">
<form action="/TestAction" method="post">
<input id="MyId" name="MyId" type="hidden" value="{{MyId}}" />
<label for="Test">Test: (optional)</label>
<select id="Test" name="Test">
<option value="">-- Choose Test --</option>
{{#each values}}
<option {{bindAttr value="id" selected="selected"}}>{{label}}</option>
{{/each}}
</select>
</form>
</div>
</script>
The JS part:
App.MyView = Ember.View.extend
templateName: 'my-template'
MyId: 42
values: [{
id: 1,
label: '1'
}, {
id: 2,
label: '2'
}, {
id: 3,
label: '3',
selected: true
}, {
id: 4,
label: '4'
}]
You can try it # http://jsfiddle.net/MikeAski/uRUc3/
I had same problem.
$('select').val(value);
This is how I solved it, I set the desired value with jQuery after rendering the template. Was surprisingly easy. (Maybe its also a way to keep the templates logicless)

Resources