The data being passed into the handlebars template looks like this:
a = {
x: {
name: "xavier"
},
y: {
name: "yamal"
},
}
b = {
properties: {
x: {
property: "number"
}
}
}
Handlebars template looks like this:
<div class="left-panel">
{{a.x.name}} // Prints correctly "xavier"
{{#each b.properties}}
<h4>{{#key}}</h4> // Prints correctly "x"
<h4>{{ ../a.[#key].name }}</h4> // does not print "xavier"
{{/each}}
</div>
As you can see, I want to be able to access the name of a dict in a using the key in b. How can I achieve this?
Your question is essentially the same as this one: Handlebars.js - Access object value with a variable key
The only extra detail is that you will need to make use of Handlebars subexpressions in order to perform the two lookups (first of #key; then of 'name').
A subexpression will allow you to lookup the value of #key on ../a and pass that to a second lookup of the value of 'name' on the result of the first lookup.
The relevant line in your template becomes:
<h4>{{lookup (lookup ../a #key) 'name'}}</h4>
Here is a fiddle for your reference:
https://jsfiddle.net/76484/b0puy52n/
i want to refresh/reload a part of my template after a variable change so that if the variable is true it shows a content A or else it will show content B. I'm sure this is a quite simple question but i'm having troubles on finding the solution.
Something like this:
Template.x.created = function() {
this.variable = false;
}
Template.x.helpers({
'getValue': function(){
return this.variable;
}
});
Template:
<template name="x">
{{#if getValue}}
<content A>
{{else}}
<content B>
{{/if}}
</template>
You need to create a reactive data source to get the template helper to re-run when the variable changes, as a normal variable won't let the helper know when it changes value. The simplest solution is to use ReactiveVar:
Template.x.onCreated(function() {
this.variable = new ReactiveVar(false);
});
Template.x.helpers({
'getValue': function() {
// Note that 'this' inside a template helper may not refer to the template instance
return Template.instance().variable.get();
}
});
If you need to access the value somewhere outside this template, you can use Session as an alternative reactive data source.
#Waiski answer is a good one, but I want to share a simple Template helper I build because a lot of Templates need this:
Using registerHelper you can build a global helper like so:
Template.registerHelper('get', function (key) {
let obj = Template.instance()[key]
return (obj && obj.get) ? obj.get() : obj
})
Use it in every template:
Template.x.onCreated(function() {
this.foo = new ReactiveVar(true)
this.bar = new ReactiveVar('abc')
})
Html:
{{#let foo=(get 'foo')}}
{{#if get 'bar'}}
Bar is true. Foo: {{foo}}
{{/if}}
{{/let}}
I am creating a custom handlebar helper but it always throws Object #<Object> has no method 'fn' when compiled through the terminal.
My handlebar helper is:
module.exports.register = function (Handlebars, opts, params) {
Handlebars.registerHelper('compimg', function (context, opts) {
var compImg = ["assets/img/icon-nope.png","assets/img/icon-check.png"];
return compImg[opts.fn(context)];
});
}
My .hbs file is:
{{#each checkable}}
<div class="col-md-3 col-xs-3 icon-container"><img src="{{compimg this}}"></div>
{{/each}}
My JSON file is:
{
"desc": "blablabla",
"checkable": [
1,
1,
1,
1
]
}
When I checked the official documentation I found this piece of code. Can someone explain what exactly context and options are here?
Handlebars.registerHelper('each', function(context, options) {
var ret = "";
for(var i=0, j=context.length; i<j; i++) {
ret = ret + options.fn(context[i]);
}
return ret;
});
The handlebars example for each is a block helper, which means that there is more markup or template syntax between the {{#each}} and {{/each}} tags. When you use this syntax, Handlebars passes an options parameter to your helper as the last argument. The options object contains a fn method that works just like a compiled template... var html = options.fn(context); gives you the rendered template from inside the block.
The context variable is the thing that you're passing into your helper and can be any number of arguments if you want more.
Since you're making an inline helper and a not a block helper, I think you only need to change one line...
return compImg[opts.fn(context)];
to
return compImg[context];
I'm iterating over a list in Handlebars using the built-in each helper.
Within the each block, I'm referencing the current loop index {{#index}} to print the consecutive number of an item:
<script id="list-item-template" type="text/x-handlebars-template">
{{#each items}}
<li class="topcoat-list__item">
Item number {{#index}}
</li>
{{/each}}
</script>
This gives the following output:
Item number 0
Item number 1
Item number 2
....
The problem is that I want to display an offsetted index which starts with 1 instead of 0.
I tried to perform calculations on the index like {{#index+1}}, but this just leads to an
Uncaught Error: Parse error
Handlebars gives you the possibility to write a custom helper that handles this situation, e.g. a helper function that lets you perform calculations on expressions like addition and subtraction etc.
Below function registers a new helper, which simply increments a value by 1:
var Handlebars = require('handlebars');
Handlebars.registerHelper("inc", function(value, options)
{
return parseInt(value) + 1;
});
You can then use it within the handlebar expression using the inc keyword, like:
{{inc #index}}
Actual answer: https://stackoverflow.com/a/46317662/1549191
Register a math handlebar and perform all mathematical operations.
app.engine('handlebars', exphbs({
helpers:{
// Function to do basic mathematical operation in handlebar
math: function(lvalue, operator, rvalue) {lvalue = parseFloat(lvalue);
rvalue = parseFloat(rvalue);
return {
"+": lvalue + rvalue,
"-": lvalue - rvalue,
"*": lvalue * rvalue,
"/": lvalue / rvalue,
"%": lvalue % rvalue
}[operator];
}
}}));
app.set('view engine', 'handlebars');
Then you can directly perform operation in your view.
{{#each myArray}}
<span>{{math #index "+" 1}}</span>
{{/each}}
I believe you can use...
{{math #index "+" 1}}
To expand on Mobiletainment's answer, this solution allows for the value to be incremented by to be passed in as an argument. If no value is passed, then a default value of 1 is used.
Handlebars.registerHelper('inc', function(number, options) {
if(typeof(number) === 'undefined' || number === null)
return null;
// Increment by inc parameter if it exists or just by one
return number + (options.hash.inc || 1);
});
Within your template you can then write:
{{inc #index inc=2}}
I solved this issue for myself by adding a short script tag to the bottom of my handlebars code!
Add a class to wherever you are calling #index and then the below jQuery code works (can also be done using vanilla JS).
<p class="create_index">
{{#index}}
</p>
<script>
$(".create_index").text(parseInt($(".create_index").text())+1)
</script>
edit 4/28- This has changed to use vanilla JS for better backwards compatibility (i.e. IE7, 8):
<span class="create_index"></span>
<script>
var divs = document.querySelectorAll('.create_index');
for (var i = 0; i < divs.length; ++i) {
divs[i].innerHTML = i + 1;
}
</script>
document.querySelectorAll has great compatibility but could also be document.getElementsByClassName("create_index")
Throwing my solution in here. CSS counters.
body {
counter-reset: section; /* Set a counter named 'section', and its initial value is 0. */
}
h3::before {
counter-increment: section; /* Increment the value of section counter by 1 */
content: counter(section); /* Display the value of section counter */
}
I was stuck on this and it was a nicer solution compared to adding a new helper.
for more accuracy in devlopment server use this
var hbs=require('express-handlebars')
var app = express();
app.set('view engine', 'hbs');
app.engine('hbs', hbs({
helpers: {
inc: function (value, options) {
return parseInt(value) + 1;
}
},
extname: 'hbs',
defaultLayout: 'layout',
layoutsDIR: __dirname + '/views/layout/',
partialsDIR: __dirname + '/views/partials/'
}))
this part create a function called inc for increasing the value of {{#index}} by one
app.engine('hbs', hbs({
helpers: {
inc: function (value, options) {
return parseInt(value) + 1;
}
},
we can use inc function with {{#index}} like this {{inc #index}}
here i include a screenshot of one of my project hosted in glitch.com and the link is given below
https://library-management-system-joel.glitch.me/view-books
(this project is under construction)
thanks to Mobiletainment and Drkawashima
The handlebars-helpers library has a fairly thorough mathematics library in lib/math.js, including a general purpose {{add a b}} helper defined as follows:
/**
* Return the product of a plus b.
*
* #param {Number} a
* #param {Number} b
* #api public
*/
helpers.add = function(a, b) {
return a + b;
};
If you don't want to copy and paste this into your project and you have the possibility to use npm, you can get this dependency as follows:
npm install handlebars-helpers --save
Then, you can register the math helpers as follows:
const handlebars = require('handlebars'),
handlebarsHelpers = require('handlebars-helpers');
handlebarsHelpers.math({
handlebars: handlebars
});
I was using nodejs and express-handlebars as template engine and facing same problem. And this is how I managed to solve.
You can create a folder and a js file inside it where you can create your own custom helpers that takes index and returns incrementing it by 1.
module.exports = {
formatIndex: function(index) {
return index+1;
}
}
Remember to register helper in your application(in my case app.js). I have used express-handlebars so I have reistered helper in this way:
app.engine('handlebars', exphbs({defaultLayout: 'home', helpers: { formatIndex }}));
Note: You have to import formatIndex before registering.
Then you can use it in your view as:
{{#each assignments}}
<div>{{formatIndex #index }}</div>
{{/if}}
Create method eval
Handlebars.registerHelper('eval', function(...e)
{
e.pop();
const args = e.join('');
return eval(args) ;
}
);
and use in any math actions
{{eval #index " + 1"}}
or
{{eval #index " + 1 - 2 * 4"}}
or
{{eval #index " + 1" "- 2" "* 4"}}
I refer in Helpers of express-handlebars document.
https://www.npmjs.com/package/express-handlebars
-----index.js-----
app.engine(
'hbs',
engine({
extname: '.hbs',
helpers: {
sum: (a, b) => a + b,
},
}),
);
-----handlebars file-----
<tbody>
{{#each courses}}
<tr>
<th scope="row">{{sum #index 1}}</th>
<td>{{this.name}}</td>
<td>{{this.description}}</td>
<td>{{this.createdAt}}</td>
</tr>
{{/each}}
</tbody>
there is an easy way to incitement with the {{#index}} value.
solution is to call the funtion and pass the #index value o it and than on JS, use helper to return #index + 1
here is go
{{incitement #index}}
and here is code for the js
Handlebars.registerHelper("incitement", function (inindex) {
return inindex + 1
});
Say I have JSON:
{
userinput: [
{name: "brian", "value": "i like pies"},
{name: "susan", "value": "memes are stupid"}
],
feedback: [
{value: "i also like pies"},
{value: "null"}
]
}
And I'm trying to draw a table like this:
name ..... | input ...... | feedback
-----------|----------------|-----------------
brian | I like pies | I also like pies
susan | mems are stupid| null
And while I recognise that it would be better to have feedback as a value of "userinput", what I have is not done like that ...
I'm trying to get the index of feedback inside {{#each userinput}}`, e.g.
{{#each userinput}}
<td>{{name}}</td><td>{{value}}</td><td>{{../feedback[#index].value}}</td>
{{/each}}
But of course {{../feedback[#index].value}} does not work.
What is the best way (without changing the structure of the json) to grab the value of the matching index inside the feedback array?
This can be accomplished using the lookup helper:
The lookup helper allows for dynamic parameter resolution using Handlebars variables. This is useful for resolving values for array indexes.
So the template for your example would look like this:
{{#each userinput}}
<td>{{name}}</td>
<td>{{value}}</td>
<td>
{{#with (lookup ../feedback #index)}}
{{value}}
{{/with}}
</td>
{{/each}}
I guess you will have to write a block helper for this, as it seems #index can only be used as a stand-alone.
I modified the "list" example, to allow a template like this: "{{#list userinput feedback}}<td>{{name}}</td><td>{{value}}</td><td>{{#feedback.value}}</td>{{/list}}". The implementation is like this, accepting two parameters "input" and "feedback" (plus the standard "options").
Handlebars.registerHelper('list', function(input, feedback, options) {
var out = "", data;
// iterate over the input
for (var i=0; i<input.length; i++) {
if (options.data) {
data = Handlebars.createFrame(options.data || {});
// add "feedback" item to the current frame's data
data.feedback = feedback[i];
}
out += "<tr>" + options.fn(input[i], { data: data }) + "</tr>";
}
return out;
});
Here's the Fiddle.