Let's say I have a template in Meteor that is generating multiple card panels using Materialize. The key idea is that it's generating unique values inside each panel.
<template name="Teacher_Information">
{{#each TeacherNames}}
<div class="row">
<div class="col s12">
<div class="card blue-grey darken-1">
<div class="card-content white-text">
<img src={{formatImage FullName}} alt="" class="circle">
<span class="card-title">{{formatName FullName}}</span>
<span>Some text here.</span>
<span>
<form>
<input name="dayGroup" type="radio" id="teach-sunday-{{#index}}" value="Sunday">
<label for="teach-sunday-{{#index}}" class="white-text">SU</label>
<input name="dayGroup" type="radio" id="teach-monday-{{#index}}" value="Monday">
<label for="teach-monday-{{#index}}" class="white-text">MO</label>
<input name="dayGroup" type="radio" id="teach-tuesday-{{#index}}" value="Tuesday">
<label for="teach-tuesday-{{#index}}" class="white-text">TU</label>
<input name="dayGroup" type="radio" id="teach-wednesday-{{#index}}" value="Wednesday">
<label for="teach-wednesday-{{#index}}" class="white-text">WE</label>
<input name="dayGroup" type="radio" id="teach-thursday-{{#index}}" value="Thursday">
<label for="teach-thursday-{{#index}}" class="white-text">TH</label>
<input name="dayGroup" type="radio" id="teach-friday-{{#index}}" value="Friday">
<label for="teach-friday-{{#index}}" class="white-text">FR</label>
</form>
</span>
</div>
<div class="card-action white-text">
<form class="input-field col s6 card-selector">
<select multiple id="teacher-students-day1-{{#index}}">
<option value="" disabled selected>Select Students</option>
{{#each StudentList1}}
<option value= '{{FullName}}'>{{formatName FullName}} ({{Level}})</option>
{{/each}}
</select>
</form>
<form class="input-field col s2 card-selector">
<select multiple id="days-off-{{#index}}">
<option value="" disabled selected>Off</option>
<option value="Sunday">Su</option>
<option value="Monday">Mo</option>
<option value="Tuesday">Tu</option>
<option value="Wednesday">We</option>
<option value="Thursday">Th</option>
<option value="Friday">Fr</option>
<option value="Saturday">Sa</option>
</select>
</form>
</div>
</div>
</div>
</div>
{{/each}}
</template>
Depending on the length of 'TeacherNames' it will create that many panels. It uses the #index syntax to produce unique names.
Now when I need to create the events template, what I'd need would be something like:
Template.Teacher_Information.events({
'click #img-0': function() {
// do some stuff
},
'click #image-1': function() {
// some more stuff
},
...
...
});
I can't insert a for loop inside the template to generate the key:value pairs, so I'm pretty much at a standstill on how I could generate a dynamic events template like that.
The normal pattern for doing this is to simply define a template at the lower level and then attach events to that. In your case:
<template name="Teacher_Information">
{{#each TeacherNames}}
{{> oneTeacher}}
{{/each}}
</template>
Then your oneTeacher template would be everything you had inside the {{#each TeacherNames}} loop before.
Now you can attach events to the oneTeacher template and get the data context of the teacher in your event:
Template.oneTeacher.events({
'click a'(ev){
console.log(this); // 'this' will be a teacher object
}
});
Understanding this was one of the early "OMG this is so much simpler!" moments when I was learning Meteor.
While you're at it you can also DRY up your days of week template by looping over an array you build in code:
Template.oneTeacher.helpers({
dow() { return [
{ number: 1, day: "Sunday", abbr: "SU" },
{ number: 2, day: "Monday", abbr: "MO" },
{ number: 3, day: "Tuesday", abbr: "TU" },
{ number: 4, day: "Wednesday", abbr: "WE" },
{ number: 5, day: "Thursday", abbr: "TH" },
{ number: 6, day: "Friday", abbr: "FR" },
{ number: 7, day: "Saturday", abbr: "SA" }
];
}
});
Then you can do:
<form>
{{#each dow}}
<input name="dayGroup" type="radio" value={{name}}>
<label class="white-text">{{abbr}}</label>
{{/each}}
</form>
You'll find that you won't really need to give every DOM object unique IDs when you are handling events at the lowest levels.
Related
I'm stumped as to why part of my code isn't displaying when the session variable is set to true. When the user selects a certain option from the dropdown, based on the value, it changes the warmerselected to TRUE. I have tracked this in the console, and it works just fine.
Here is my code:
HTML:
<div class="item">
<div class="ui label">Product Type:</div>
<select id="ProductType" name="ProductType">
{{#each ProductTypes}}
<option value="{{ProductTypeAbbrev}}">{{ProductTypeName}}</option>
{{/each}}
</select>
</div>
{{#if warmerselected}}
<div class="item">
<div class="ui label">Name:</div>
<input type="text" name="ProductName" placeholder="Enter Product Name">
</div>
{{/if}}
JS:
Session.setDefault("warmerselected", false);
Template.addProduct.events({
'change #ProductType': function (e) {
var selitem = $("#ProductType").val();
if(selitem == "WA") {
Session.set("warmerselected",true)
}else {
Session.set("warmerselected",false);
}
}
});
Template.addProduct.helpers({
warmerselected: function(){
return Session.get("warmerselected");
}
});
Session isn't meant to be helper in HTML, you have to create one
I have this block of code in a Spacebars template:
1.
<select class="form-group">
{{#each choices}}
<option>{{this}}</option>
{{/each}}
</select>
I would like to repeat this N times incrementing the number each time like so:
1.
<select class="form-group">
{{#each choices}}
<option>{{this}}</option>
{{/each}}
</select>
2.
<select class="form-group">
{{#each choices}}
<option>{{this}}</option>
{{/each}}
</select>
3.
<select class="form-group">
{{#each choices}}
<option>{{this}}</option>
{{/each}}
</select>
I would love to be able to pass N to a custom template tag to take care of this (e.g. {{choices 3}}). What's a nice DRY way to do this? I have a vague notion I could write a template helper, but I'm not sure where to start.
Working Example:
http://meteorpad.com/pad/THAQfpfrru5MgAGnS/Copy%20of%20Leaderboard
You can pass a count in and return an array of arbitrary objects. Not the most elegant... but it worked!
HTML
<body>
{{>content}}
</body>
<template name="content">
{{#each loopCount 5}}
<select class="form-group">
{{#each choices}}
<option>{{this}}</option>
{{/each}}
</select>
{{/each}}
</template>
JS
Template.content.helpers({
choices: function(){
return ['choice1','choice2','choice3']
},
loopCount: function(count){
var countArr = [];
for (var i=0; i<count; i++){
countArr.push({});
}
return countArr;
}
});
If you're using the underscore package for Meteor, and also happen to be using CoffeScript, you can create the following single-line template helper:
t.helpers
loop: (count) -> {} for i in _.range count
You can then use this helper in your template:
{{! Will display 'Output' 5 times }}
{{#each loop 5}}
Output
{{/each}}
Here is part of my form that I'd like to convert to using autoform.
<div class="col-lg-4 col-md-4 col-sm-4">
<label for="pay_with" id="pay_with_label">Pay With</label>
<select name="pay_with" id="pay_with" class="form-control select select-primary mbl" required data-placeholder="Select an Option">
<option value="Card">New Card</option>
<option value="Check">New Bank</option>
{{#each device}}
<option value="{{card_id}}" selected {{selected}}>{{brand}} - {{last4}}</option>
{{/each}}
</select>
In autoform, how do I translate this part?
{{#each device}}
<option value="{{card_id}}" selected {{selected}}>{{brand}} - {{last4}}</option>
{{/each}}
Create a template helper function that returns the options array for your select menu, by mapping your devices array and adding in the extra options in order to get the proper schema for autoform, like so:
Template.myForm.helpers({
deviceOptions: function() {
var deviceOpts = devices.map(function(device) {
return { label: device.brand+' - '+device.last4, value: device.card_id }
});
deviceOpts.unshift({ label: 'New bank', value: 'Check' });
deviceOpts.unshift({ label: 'New card', value: 'Card' });
return deviceOpts;
},
});
Then, you can call the helper in your template directive:
{{> afFieldInput name="payWith" type="select" options=deviceOptions }}
I am doing simple validation of inputs in meteorjs, after first tour it works, and every next time it doesn't work (until I reload the page) – it means error messages are not displayed.
//main.js//
Template.addMealForm.events({
'click #submitNewMeal': function (ev) {
ev.preventDefault();
var query = {
name: $("#name").val().trim(),
price: $("#price").val(),
calories: $("#calories").val(),
category: $("#category").val()
};
areInputsValid(query);
}
});
var areInputsValid = function (query) {
if ((query.name.length === 0) || (query.price.length === 0) || (query.calories.length === 0)) {
$("#warningLabel").addClass("di")
$(".warningLabel").text("All fields are required");
}
else if ((isNaN(query.price) === true) || (isNaN(query.calories) === true)) {
$("#warningLabel").addClass("di")
$(".warningLabel").text("To Price and Calories fields please enter a number");
}
else {
console.log('it works');
$('.dn').hide();
}
};
//main.html//
<template name="addMealForm">
<form role="form">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control input_form" id="name" placeholder="Name of the meal">
</div>
<div class="form-group">
<label for="price">Price</label>
<input class="form-control input_form" id="price" placeholder="Price">
</div>
<div class="form-group">
<label for="calories">Calories</label>
<input class="form-control input_form" id="calories" placeholder="Calories">
</div>
<div id="warningLabel" class="form-group has-error dn">
<label class="control-label warningLabel"></label>
</div>
<button id="submitNewMeal" type="submit" class="btn btn-rimary">Add</button>
</form>
</template>
The problem is that you are calling $('.dn').hide() in the success case. Because #warningLabel has a class of dn it will not be displayed again on subsequent errors.
One solution is to add $('.dn').show() to the top of areInputsValid.
You already have Tracker as part of Meteor, so I put a little tutorial and JSfiddle together on how to use it to implement a typical form validation scenario.
http://bit.ly/meteor-form-validation-video
http://bit.ly/meteor-form-validation-fiddle
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)