Using Handlebars.js to iterate over a list of names - javascript

I have a Bootstrap select dropdown, and I'm attempting to use Handlebars JS to output the desired options into my selection, but they aren't appearing. I've read some examples here so not sure what is stopping my code from correctly rendering, any pointers would be appreciated.
HTML:
<select class="custom-select" id="inputGroupSelect01" onChange="takeSelectedSourceValue(this);">
<option id="source-language" value="" selected disabled>Auto</option>
<div id="myForm"></div>
<script type="text/handlebar-template" id="selectPersonTemplate">
{{#each people}}
<option value="{{valueLang}}">{{htmlLang}}</option>
{{/each}}
</select>
</script>
<script src="https://twitter.github.io/typeahead.js/js/handlebars.js"></script>
JS:
$(function () {
// Define an array of people.
var people = [
{ valueLang: 'DE', htmlLang: 'DE' },
{ valueLang: 'FR', htmlLang: 'FR' },
{ valueLang: 'IT', htmlLang: 'IT' },
{ valueLang: 'ES', htmlLang: 'ES' },
{ valueLang: 'SV', htmlLang: 'SV' },
{ valueLang: 'DA', htmlLang: 'DA' }
];
// Get the text for the Handlebars template from the script element.
var templateText = $("#selectPersonTemplate").html();
// Compile the Handlebars template.
var selectPersonTemplate = Handlebars.compile(templateText);
// Evaluate the template with an array of people.
var html = selectPersonTemplate({ people: people });
// Take the HTML that was created with the Handlebars template and
// set the HTML in the myForm div element.
$("#myForm").html(html);
});

There are a few things wrong with your HTML.
First, your closing select tag, </select> is within your template script tag.
What is within your script tag is ignored by the HTML parser because you have defined the script as type="text/handlebar-template". Therefore, the parser does not see a closing select tag.
Second, <div> elements are not valid children of select, <select>, elements.
I am not sure if all browsers handle this invalid tag the same way, but I am trying it using Chrome and it seems that Chrome is completely ignoring the <div id="myForm"></div> when rendering. So there is no element to which to add the html produced by your template function. The html value should be appended to the <select>.
I would advise taking the template <script> tag out from within the <select>, just for clarity. This will make the HTML easier to read and clearly separate the <select> menu from the template.
<select class="custom-select" id="inputGroupSelect01" onChange="takeSelectedSourceValue(this);">
<option id="source-language" value="" selected disabled>Auto</option>
</select>
<script type="text/handlebar-template" id="selectPersonTemplate">
{{#each people}}
<option value="{{valueLang}}">{{htmlLang}}</option>
{{/each}}
</script>
Next, we want to be sure to append html to our #inputGroupSelect01 element.
// append the HTML to the #inputGroupSelect01 select element.
$("#inputGroupSelect01").append(html);
I have created a fiddle for your reference.

The answer form Steve is perfectly fine and his fiddle also works fine. I would like to add some comments though and simplify the result:
About the closing tag </select>: Whenever you want to extend your existing HTML with Handlebars rendered HTML, you need to start with a valid HTML. In your case the browser already rendered the "invalid" <select> (invalid because the closing tag was missing). Most browsers then try to fix this by adding the closing tag,... but with your template you tried to add another one, and this doesn't work. I can see why you did this as you wanted to "complete" the <select> tag within your template, but this was too late already as the tag was rendered before your template got a chance to do so.
Conclusion: before your javaScript kicks in, the HTML is already rendered, even though it might be invalid HTML, so you need to make sure that it is valid before you manipulate the DOM.
The rest of what was going on (or wrong) was described by Steve...
To keep things simple I would probably even add the whole <select> inside the template so you need less references to HTMLElements and avoid complexity:
<script type="text/handlebar-template" id="selectPersonTemplate">
<select class="custom-select" onChange="takeSelectedSourceValue(this);">
<option id="source-language" value="" selected disabled>Auto</option>
{{#each people}}
<option value="{{valueLang}}">{{htmlLang}}</option>
{{/each}}
</select>
</script>
and then the JS (only parts of the JS from the fiddle):
// Get the template for Handlebars.
// Put whole <select> inside template to keep things simple
var template = $("#selectPersonTemplate");
// Compile the Handlebars template.
var selectPersonTemplate = Handlebars.compile(template.html());
// Evaluate the template with an array of people.
var html = selectPersonTemplate({ people: people });
// Two ways of attaching the HTML to the DOM
// $(html).insertAfter(template);
template.replaceWith(html); // removes script at the same time
By just looking at the template and the code you might realise that everything seems to be clear and simple and not spread around any more.
Here the slightly changed fiddle from Steve.

Related

Dynamically update Javascript variables using Jinja?

I have a web form created that requires template creation by the user. Calling all previous entries that are templates isn't an issue, but passing the information to the form when called seems to be tricky. Basically I want the user to be able to select a template from the dropdown (See the screenshots and stuff below), then based on their selection, update the variables in the script to autofill form data. Right now, it only selects the most recent entry. Is this possible just using python/flask/javascript or am I missing something? Any help would be appreciated! Thanks!
Template Dropdown
<label for="template_select">Select Template</label>
<select class="form-control" name="template_select" id='template_select' onchange='changeTemplate()'>
<option value=''></option>
{% for template_info in template_search %}
<option value='{{template_info.client_name}}'>{{template_info.client_name}}</option>
{% endfor %}
</select>
Javascript to change values
{% for template_info in template_search %}
<script>
function changeTemplate(){
var template = document.getElementById('template_select').value;
document.getElementById('client_name').value='{{template_info.client_name}}';
document.getElementById('client_name').innerHTML='{{template_info.client_name}}';
}
</script>
{% endfor %}
Python Passing in the Query
template_search = newmatter_model.NewClientMatter.query.filter_by(
creator=verifier, is_template='on').all()
Your mistake is to create Javascript code in a loop. You don't need to do this.
What you want to do is think of the data sent to the browser as independent. Make it work first without Flask and Jinja2. Create a static page that works and does what you want.
The following code would work with static data:
function changeTemplate(){
var template = document.getElementById('template_select').value;
document.getElementById('client_name').innerHTML = template;
}
<label for="template_select">Select Template</label>
<select class="form-control" name="template_select" id="template_select" onchange="changeTemplate()">
<option value=""></option>
<option value="Client 1">Client 1</option>
<option value="Client 2">Client 2</option>
<option value="Client 3">Client 3</option>
</select>
<div id="client_name"><i>No client set</i></div>
That's HTML for a select box, a separate <div> element, and Javascript code to copy the selected option value into the <div>. Note that the Javascript code doesn't know anything about what data is involved; no client names are stored in the code. All that the small function does is to copy the value from the currently selected option, to somewhere else.
Once that works on its own you can start thinking about how you are going to insert the values from your application. In the above code, all that needs replacing is the dropdown options, because the Javascript code can then access everything it needs from the <select> element value.
So the Javascript code doesn't need to be generated at all, you only generate the <option> elements, as you already did in your question.
You rarely need to generate dynamic Javascript code, and it would be much better for your app if you don't. Static Javascript code can be served by a CDN and cached in the browser, removing the need for your app to keep serving that again and again for all clients. Try to minimise that whenever you can.
Instead, generate just the data that the code needs to operate on.
You can put that information in HTML tags. In the above example, your data is put in the repeated <option> tags. Or
you could add data attributes to your HTML, which are accessible both to Javascript code and to CSS. Or
use AJAX to load data asynchronously; e.g. when you pick an option in the <select> box, use Javascript and AJAX to call a separate endpoint on your Flask server that serves more data as JSON or ready-made HTML, then have the Javascript code update your webpage based on that. Or
generate JSON data and put it directly into your HTML page. Just a <script>some_variable_name = {{datastructure|tojson|safe}};<script> section is enough; then access that some_variable_name from your static Javascript code to do interesting things on the page. JSON is a (almost entirely a) subset of Javascript, and the way the tojson filter works is guaranteed to produce a Javascript-compatible data structure for you. The browser will load it just like any other embedded Javascript code.

how to choose selected option in select/option in HTML?

I need to display two select lists with the same options (there are many).
I have the index.html:
<body>
<select class="c_select1"></select>
<select class="c_select2"></select>
</body>
and in another html file (options.html) all the options (only the options):
<option value='AED' title='United Arab Emirates Dirham'>AED</option>
<option value='AFN' title='Afghan Afghani'>AFN</option>
<option value='ALL' title='Albanian Lek'>ALL</option>
<option value='AMD' title='Armenian Dram'>AMD</option>
<option value='ANG' title='Netherlands Antillean Guilder'>ANG</option>
...
<option value='AOA' title='Angolan Kwanza'>AOA</option>
<option value='ARS' title='Argentine Peso'>ARS</option>
<option value='AUD' title='Australian Dollar'>AUD</option>
<option value='AWG' title='Aruban Florin'>AWG</option>
<option value='AZN' title='Azerbaijani Manat'>AZN</option>
Then, using jQuery, I load options inside the two select tags with the same instruction with different class selector:
$(".c_select1").load("options.html");
I'd like to display as default selected option two different values (since I need to show a currency converter).
I've tried in 2 different ways, but any of these works fine (I show the code for just one).
<script type="text/javascript">
$(".c_select1").load("options.html");
$(".c_select1 option[value=ARS]").prop("selected", true);
</script>
or
$(".c_select1").val("ARS");
I don't know why, but I think there is a problem with the .load() of jQuery.
The only solutions that work are 2:
set a timer and execute the second jQuery statement after this timer
use 2 different options.html files one for each select tag, but I think it is not so clever.
Is there a better way to do this dynamically?
Try this:
$(".c_select1").load("options.html", function(){
$(".c_select1").val("ARS");
});
Explained:
The .load() jQuery method requires some time to get the contents of options.html (even if its just a few milliseconds). The browser doesn't wait for it before continuing to run the rest of the javascript logic. The function(){ I added to your .load( method call is some javascript logic that runs AFTER the results have returned (aka a "callback").
Use a callback :
$(".c_select1").load("options.html", function() {
$(".c_select1").val("ARS");
});
$(".c_select2").load("options.html", function() {
$(".c_select2").val("ARS");
});
Actually there is a delay of load method that is why selected item is not working. Like #Joulss said use callback method that should work. You can see the here.
$(function(){
$(".c_select1").load("option.html", function() {
$(".c_select1").val("AFN");
});
$(".c_select2").load("option.html", function() {
$(".c_select2").val("ANG");
});
});
Demo

select2 set preloaded tags from html option

I have the following html, the <option> tags are generated by me (by php, more precisely symfony3)
<select
name="simple_event[tagsList][]"
id="js-tags-list"
class="form-control"
multiple
data-tags="true"
data-placeholder="add-category-separated-by-semicolon"
>
<option selected>html</option>
<option selected>select2</option>
<option selected>jquery</option>
</select>
I use select2 with the followig snippets
$("#js-tags-list").select2({tokenSeparators: [";"]});
I thought it would have put the 3 tags as preloaded data, but it actually only put them as preloaded "suggestions" (which I thought would have been the case if I had not put the selected attribute)
Is there a way to do this (i.e preload data) without needing to generate my javascript with my php (which seems really dirty to me) ?
If you load the values from a database, Symfony already provides something that preloads data. http://symfony.com/doc/current/reference/forms/types/entity.html

manipulate DOM after Blaze is done with rendering

In my meteor application I want to preload a select option list with values from my collection.
In my template I tried this, which works:
{{#each items}}
<p>
{{value}}
{{title}}
</p>
{{/each}}
The above code represents just an pre stage version of the actual code.
Then, I tried this:
<select id="category">
<option value="" disabled selected>Bitte wählen</option>
{{#each items}}
<option value="{{value}}" disabled selected>{{title}}</option>
{{/each}}
</select>
Unfortunately, it won't fill the list with any data.
I'm using materialize as UI framework, and what materialize would do is rendering the select option as a ul li unordered list instead of rendering the select option to make the user able to choose between the values. The select option list has to be initialized as follow:
$(document).ready(function() {
$('select').material_select();
});
Apparantely, $(document).ready() will be fired before blaze is done with rendering the contents. How can I force the material initialization to be run just after blaze is done adding the options to the select list?
What are you looking is the onRendered method
Template. category.onRendered(function(){
//manipulate DOM here
});
Sometimes you will need to use a timeout like this.
Template. category.onRendered(function(){
Meteor.setTimeout(function(){
//Manipulate DOM here
// this is most like a hack but it works some of the cases
},0);
});

Dynamically adding checkboxes to multiselect dropdown

I have been stuck on this problem for hours and I am going mad ! I need a dropdown of checkboxes - which I populate dynamically into a select tag. I also need to append each multiselect dropdown that I deep-clone with jquery to a number of <div> elements. However, every time I do this the cloned element is rendered as a list of multiselectable items (and not as a dropdown and loses all its styling). This is the multiselect container that I would like to add my checkboxes to:
<select class="multiselect1" multiple="multiple">
</select>
I finally initialize each cloned dropdown by calling .multiselect(); The library I am using for this is: http://davidstutz.github.io/bootstrap-multiselect/
$('.multiselect1').multiselect();
var filterClone = $('.multiselect1').clone(true);
//filterClone.multiselect();
$('body').append(filterClone[0]);
When the above lines execute, the select element is indeed present in the body but is invisible. When I remove the style attribute the element becomes visible but is rendered as a list of multiselectable items (which is expected). But why is the cloned multiselectable dropdown not displayed at all in the first place ?
Any suggestions that could lead me to a solution (or the solutions itself!) using javascript or jquery would be most appreciated.
Well to make this work you need JQuery. Did you include JQuery? If you didn't you can use this: <script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script> Did you upload all the files to your server?
Try this code on your website. Does it work?
HTML:
<select id="SOExample" multiple="multiple">
<option value="Love it!">Love it!</option>
<option value="Hate it!">Hate it!</option>
<option value="I don't know...">I don't know...</option>
</select>
JS:
<script type="text/javascript">
$(document).ready(function() {
$('#SOExample').multiselect();
});
</script>

Categories

Resources