manipulate DOM after Blaze is done with rendering - javascript

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);
});

Related

Using Handlebars.js to iterate over a list of names

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.

Polymer, IE11 dom-repeat no rendering option tags

I'm having issues specifically with IE11. This code works fine in Chrome/Firefox/Edge. I don't see what I am doing that would cause this to not work.
Here is the part of my polymer element's shadow dom that is breaking:
<template is="dom-if" if="[[showSelect]]]">
<select value="{{selectValue::change}}">
<option value="0" selected$="{{matchesDefault(0)}}">All</option>
<template is="dom-repeat" items="{{excludeNumbers}}" as="number">
<option value="[[number]]" selected$="[[isExclude(number)]]">Skip [[number]]s</option>
</template>
</select>
</template>
What I'm attempting to do and is working in Chrome/FF/Edge but not IE11:
If is set, show the shadow dom <select>.
Bind this.selectValue to update when the dropdown is changed.
Loop through this.excludeNumbers and write them as <option> nested inside of the <select>, selecting them if they are
What I've tested so far:
Removing all attributes from the <option> tags
Result: No change, still do not render
Removing the default <option value="0" selected$="{{matchesDefault(0)}}">All</option> from the markup
Result: No change, still do not render
Moving the <template is="dom-repeat"> markup outside of the <select>, just under the opening <template> tag that checks is [[showSelect]] is set & changing them from <option> to <span> tags.
Result: Successfully writes span tags
Running polymer build and testing IE10 on the built code
Result: No change
Does anyone see what I have wrong? Any ideas on what I can test?
Any ideas are appreciated
It is a known issue, tables and selects do not work with dome-repeat in IE11
There is an issue for this opened in Github here with no working solution
One solution is to use a custom element, maybe something like paper-dropdown-menu

Default dummy item with angularjs ngoptions

I'm populating an HTML select element with options loaded from an external datasource.
This is working fine but I would like to have a default "dummy" value at the top. Eg. "--Select something--".
How can this be achieved?
<select ng-Model="selectedModel" ng-options="model as model.Model__c for model in vehicleModels"></select>
you can use an ng-init as given below.
<select ng-init="selectedModel=vehicleModels[0]" ng-Model="selectedModel" ng-options="model as model.Model__c for model in vehicleModels"></select>
and have your dummy item as the first element in the vehicleModels array

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>

Using AngularJS, how do I bind a <select> element to an attribute that updates via a service

I have a display controller and a management controller. Inside my display controller, I have a dropdown selector with the list of items that have been selected.
I can get the display area dropdown to update the list, adding items as they are added in the management controller, but I cannot figure out how to select the newest item in the dropdown.
<div ng-controller="MyDisplayCtrl">
<select ng-model="item" ng-options="i.name for i in items">
</select>
</div>
I have made a jsfiddle to illustrate my situation. Ultimately, though, my question is how to bind that ng-model="item" to a data source updated by a service.
http://jsfiddle.net/whtevn/mUhPW/2/
Well, it looks like I've found a pretty satisfactory answer to this.
I exposed the storage object itself through the controller
function MyDisplayCtrl($scope, ItemStore) {
$scope.items = ItemStore.items;
$scope.item = ItemStore.currentItem;
// expose the itemstore service to the dom
$scope.store = ItemStore
$scope.getItem = function(){
return(ItemStore.currentItem);
}
}
and then address the currentItem directly
<div ng-controller="MyDisplayCtrl">
<select ng-model="store.currentItem" ng-options="i.name for i in items">
</select>
</div>
http://jsfiddle.net/whtevn/Cp2RJ/3/
Try using ng-options:
<div ng-controller="MyDisplayCtrl">
<select ng-options="i.name for i in items"></select>
</div>
See: http://docs.angularjs.org/api/ng.directive:select

Categories

Resources