Escaping curly braces in a label in handlebars - javascript

I have handlebar code that prints out a list of labels.
This values are:
{{#each theObj.Options}}
{{#if #last}}
{{this.valueLabel}}
{/if}}
{{#unless #last}}
{{this.valueLabel},
{{/unless}}
{{/each}}.
</p>{{/compare}}
The valueLabels are:
Value A
Value B
{Test}
The handlebar code prints out:
The values are: Value A, Value B, .
I need to get the last {Test} to print out. How do I do that? The printout should be:
The values are: Value A, Value B, {Test}.
Thanks.

Setting this up in a quick Code Pen seemed to yield the desired result. This is with three caveats:
I added an extra '{' to line 7 of your HTML, previously: '{/if}}'.
I removed the closing '{{/compare}}' tag as there was no opening one included in your example.
I created an object 'theObj' with a guess at a reasonable example of what should be included in there.
Could you clarify what is in the 'theObj' object so we can troubleshoot that?
HTML
<div id="app"></div>
<script id="entry-template" type="text/x-handlebars-template">
This values are:
{{#each theObj.Options}}
{{#if #last}}
{{this.valueLabel}}
{{/if}}
{{#unless #last}}
{{this.valueLabel}},
{{/unless}}
{{/each}}.
</script>
JavaScript
var theObj = {
Options: [{valueLabel: 'Value A'}, {valueLabel: 'Value B'}, {valueLabel: '{Test}'}]
};
var source = document.getElementById('entry-template').innerHTML;
var template = Handlebars.compile(source);
var context = {theObj: theObj};
var html = template(context);
document.getElementById('app').innerHTML = html;
console.log(html);
You can check out the Code Pen example here:
See the Pen Escaping curly braces in a label in handlebars by Nicholas Lawrence (#nicholaslawrencestudio) on CodePen.

Related

Meteor - how to output checkbox value's with Meteor.js?

I'm having a problem with outputting the value of selected checkbox's with Meteor.js. The checkbox value is outputting as [object Object] in the browser. Could someone please help me out.
HTML
<head>
<title>project</title>
</head>
<body>
{{>addStatusForm}}
</body>
<template name="addStatusForm">
<form class="addStatus">
{{#each category}}
<input type="checkbox" name="categoryCheckbox" class="boxCheck" value={{categoryDesc}}>{{categoryDesc}}<br>
{{/each}}
<input type="text" name="status">
<input type="submit" value="Add status">
</form>
{{#each status}}
<p>{{statusDesc}} {{category}}</p>
{{/each}}
</template>
Helpers
Template.addStatusForm.helpers({
status: function () {
return Status.find();
},
category: function(){
return Category.find();
}
});
Events
Status = new Mongo.Collection('status');
Category = new Mongo.Collection('category');
Template.addStatusForm.events({
'submit .addStatus': function (event) {
event.preventDefault();
var statusInput = event.target.status.value;
var categorySelected = $('.boxCheck:checked').val();
//var categorySelected = event.target.categoryCheckbox.value; tried this
//var categorySelectedString = JSON.stringify(categorySelected); tried this also
//console.log(categorySelected); just testing console output
//console.log(statusInput); just testing console output
Status.insert({
statusDesc : statusInput,
category : categorySelected
});
}
Problem with this is not how it is stored (console.log(typeof categorySelected ) shows that it is a string and not an object) it is that you are defining the 'category' field twice within this template when displaying the data, once from a helper and once as a field inside of the collection object. It gives the helper priority over the collection data so you get the object being returned from return Category.find(); which is an object and hence the [object Object] output
Two quick solutions here:
A) Change the name of the category helper to categories or something else
B) (Possibly more meteoric) move the code inside your each into a status template giving it a bit of isolation so it can't see the parent templates category field
{{#each status}}
<p>{{statusDesc}} {{category}}</p>
{{/each}}
/*...BECOMES...*/
{{#each status}}
{{>statusTemplate}}
{{/each}}
<template name="statusTemplate">
<p>{{statusDesc}} {{category}}</p>
</template>

Evaluate an expression from within a JSON file

I have an Angular app where data is loaded in through JSON files. For the various objects, one of the properties is a "Description". In my app I pop it in my html via {{item.Description}}. My problem is that the string in the JSON file has values that need to be adjusted based on a variable. For example, "The value is 160 (+20 per var)". I would like this description to read out 160 plus 20 times the value of the provided variable.
Unfortunately I can't just put {{160+(20*var)}} in the description, because it just prints out the expression as string.
Is there anyway to create that binding in angular so it updates dynamically based on the other variable?
Update
As per request I'm adding as much code as I can.
In my file's head I'm including a JSON file with:
<script src="path/to/file.json"></script>
Then, I have my controller:
app.controller('itemController', function(){
this.items = Items //Items is declared in the JSON file as the JSON object.
});
Then in my HTML I call:
<div ng-controller="itemController as ctrl">
<span class="description" ng-repeat="item in ctrl.items">
{{item.Description}}
</span>
</div>
The problem is, that item.Description has expressions I would like to evaluate. I would normally just do {{160+(20*ctrl.var)}}, but since that expression is contained in the item.Description string, Angular doesn't evaluate it normally.
It appears you can do this by replacing {{item.Description}} with {{$eval(item.Description)}}, which will evaluate a string as an Angular expression. See the Angular docs for expressions, or a StackOverflow post about $eval.
Edit: OP has clarified that item.Description may contain mixed Angular expressions and other text, for example "The value is {{85 + 22 * ctrl.var}}". Fortunately the Angular docs for $compile contain an example that solves this exact problem! Here is a brief demo.
angular.module('app', [])
.directive('compile', function($compile) {
// directive factory creates a link function
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
// watch the 'compile' expression for changes
return scope.$eval(attrs.compile);
},
function(value) {
// when the 'compile' expression changes
// assign it into the current DOM
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
}
);
};
})
.controller('itemController', function() {
this.var = 5;
this.items = [
{Description: "What's 1+1? It's {{1+1}}"},
{Description: "The value is {{85+22*ctrl.var}}"},
{Description: "He{{'llo'}} World!"}
];
});
body {
font-family: sans-serif;
}
#main > span {
background-color: #ddd;
padding: 8px 16px;
margin: 8px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app" class="ng-scope">
<h1>Compile Test!</h1>
<div ng-controller="itemController as ctrl" id="main">
<span class="description ng-scope" ng-repeat="item in ctrl.items" compile="item.Description"></span>
</div>
</body>

Ember.js - conditionally display template content based on current model item index

I have an Ember.js app that requires me to render content in groups of 6. For example, I have a model that contains 18 'activities.' I need to group the activities in sets of six, so I'll have three groupings of six each. I know I can't use non-boolean conditionals in Handlebars, so does anyone have an idea on how to best implement the following concept?
<script type="text/x-handlerbars" data-template-name="categories">
{{#each activity in parentController.activity}}
// For every sixth item, start a new grouping
{{#if activity.index() % 6 == 0}}
<div class="activityBlock">
// Render views 1 - 6 the first time, 7 - 12 the second time, and 13 - 18 the third time
{{view "activity"}}
</div>
{{/if}}
{{/each}}
</script>
<script type="text/x-handlebars" data-template-name="activity">
{{item.title}}
</script>
Expanding on what #MatthewBlancarte said in the comments, you can do the following:
App.IndexController = Ember.ArrayController.extend({
categorySlices: function(){
var model = this.get('model')
var slice1 = model.slice(0, 6);
var slice2 = model.slice(6, 12);
var slice3 = model.slice(12);
return [slice1, slice2, slice3];
}.property('model')
})
And then, your template might look like this:
<script type='text/x-handlebars' id='index'>
{{#each slice in categorySlices}}
{{#each item in slice}}
{{ item }}<br>
{{/each}}
<p>
{{/each}}
</script>
See a working solution here

How to use a logical OR with #if

I have data handed to me by the server.
I do not want the container <p> to be present if there is no data item. But there could be several data items or 1 data item, if any exist I need a <p> wrapper.
Ideally, I am looking for some kind of #ifor helper which will accept any number of arguments. Is this possible?
{{#ifor firstname||lastname||nickname||age}}
<p>
{{#if firstname}}
<span class="firtname">{{firstname}}</span>
{{/if}}
{{#if lastname}}
{{lastname}}
{{/if}}
{{#if age}}
Age: {{age}}
{{/if}}
{{#if nickname}}
- ( {{type}} )
{{/if}}
</p>
{{/if}}
A simple solution would be to write a helper that accepts the attributes you wish to test, without the OR operator. You then iterate over the arguments and check if they match a truth test :
Handlebars.registerHelper('ifor', function() {
var l, opts;
opts = Array.prototype.pop.call(arguments);
for (l = arguments.length-1; l>=0; l--) {
// test against undefined if you prefer
//if (typeof(arguments[l])!=='undefined')
if (arguments[l])
return opts.fn(this);
}
return opts.inverse(this);
});
And a Fiddle http://jsfiddle.net/G5Vhc/1/

handlebars - is it possible to access parent context in a partial?

I've got a handlebar template that loads a partial for a sub-element.
I would need to access a variable from the parent context in the calling template, from within the partial. .. doesn't seem to resolve to anything inside the partial.
Simplified code goes like this:
the template
{{#each items}}
{{> item-template}}
{{/each}}
the partial
value is {{value}}
(obviously the real code is more complicated but it's the same principle, within the partial .. appears to be undefined.)
To show it's undefined, I've used a very simple helper whatis like this:
Handlebars.registerHelper('whatis', function(param) {
console.log(param);
});
and updated the above code to this:
updated template
{{#each items}}
{{whatis ..}} <-- Console shows the correct parent context
{{> item-template}}
{{/each}}
updated partial
{{whatis ..}} <-- Console shows "undefined"
value is {{value}}
Is there a way to go around that issue? Am I missing something?
EDIT: There's an open issue relating to this question on handlebars' github project
Just in case anyone stumbles across this question. This functionality exists now in Handlebars.
Do this:
{{#each items}}
{{! Will pass the current item in items to your partial }}
{{> item-template this}}
{{/each}}
Working fiddle (inspired by handlebars pull request #385 by AndrewHenderson)
http://jsfiddle.net/QV9em/4/
Handlebars.registerHelper('include', function(options) {
var context = {},
mergeContext = function(obj) {
for(var k in obj)context[k]=obj[k];
};
mergeContext(this);
mergeContext(options.hash);
return options.fn(context);
});
Here's how you'd setup the parent template:
{{#each items}}
{{#include parent=..}}
{{> item-template}}
{{/include}}
{{/each}}
And the partial:
value is {{parent}}
As of 2.0.0 partials now supports passing in values.
{{#each items}}
{{> item-template some_parent_var=../some_parent_var}}
{{/each}}
Took me awhile to find this, hope it's useful for someone else too!
The easiest way to pass the parent context to the partial is to do the loop inside the partial. This way the parent context is passed by default and when you do the loop inside the partial the {{../variable}} convention can access the parent context.
example fiddle here.
The Data
{
color: "#000"
items: [
{ title: "title one" },
{ title: "title two" },
]
}
The Template
<div class="mainTemplate">
Parent Color: {{color}}
{{> partial}}
</div>
The Partial
<div>
{{#each items}}
<div style="color:{{../color}}">
{{title}}
</div>
{{/each}}
</div>
You can use some of the proposed solutions on the comments from the link to github:
https://github.com/wycats/handlebars.js/issues/182#issuecomment-4206666
https://github.com/wycats/handlebars.js/issues/182#issuecomment-4445747
They create helpers to pass the info to the partial.
I created an each Helper function that includes the parent key/values within the subcontext under the key parentContext.
http://jsfiddle.net/AndrewHenderson/kQZpu/1/
Note: Underscore is a dependency.
Handlebars.registerHelper('eachIncludeParent', function ( context, options ) {
var fn = options.fn,
inverse = options.inverse,
ret = "",
_context = [];
$.each(context, function (index, object) {
var _object = $.extend({}, object);
_context.push(_object);
});
if ( _context && _context.length > 0 ) {
for ( var i = 0, j = _context.length; i < j; i++ ) {
_context[i]["parentContext"] = options.hash.parent;
ret = ret + fn(_context[i]);
}
} else {
ret = inverse(this);
}
return ret;
});
To be used as follows:
{{#eachIncludeParent context parent=this}}
{{> yourPartial}}
{{/eachIncludeParent}}
Access parent context values in your partial using {{parentContext.value}}
I needed dynamic form attributes for something like this...
{{#each model.questions}}
<h3>{{text}}</h3>
{{#each answers}}
{{formbuilder ../type id ../id text}}
{{/each}}
{{/each}}
and a helper like so...
Handlebars.registerHelper('formbuilder', function(type, id, qnum, text, options)
{
var q_type = options.contexts[0][type],
a_id = options.contexts[1].id,
q_number = options.contexts[0][qnum],
a_text = options.contexts[1].text;
return new Handlebars.SafeString(
'<input type=' + q_type + ' id=' + a_id + ' name=' + q_number + '>' + a_text + '</input><br/>'
);
});
Which produces...
<input type="checkbox" id="1" name="surveyQ0">First question</input>
My model is a big blob of arrays and objects mixed together. What's noteworthy is that using '../' like so '../type', passes in the parent model as the context, and without it, such as with 'id', it passes in the current model as the context.
To get specifically the parent of the partial (where you may be several partials deep) then follow the other answers like SeanWM.
If you know that the parent is the main template then you can use #root which resolves to the top-most context no matter how deep you are.
e.g. {{#root.rootObject.rootProperty}}
It is a pity that ../../.. does not go up past a partial.

Categories

Resources