Multiple loops fail in Handlebars.js - javascript

In my Handlebars template, I am trying to loop over the same data twice, but it fails on the second loop. This is how my template looks:
<div id="placeholder"></div>
<script type="text/x-handlebars" id="people-template">
First loop:<br />
<ul>
{{#.}}
<li>{{name}}</li>
{{/.}}
</ul>
Second loop:<br />
<ul>
{{#.}}
<li>{{name}}</li>
{{/.}}
</ul>
</script>
And this is the JavaScript:
var context= [
{ name: "John Doe", location: { city: "Chicago" } },
{ name: "Jane Doe", location: { city: "New York"} }
];
var template = Handlebars.compile($("#people-template").text());
var html = template(context);
$('#placeholder').html(html);
However, it does not render anything for the second loop:
First loop:
John Doe
Jane Doe
Second loop:
I created a jsFiddle for this here: http://jsfiddle.net/G83Pk/ and have even logged this in as a bug https://github.com/wycats/handlebars.js/issues/408. Does anyone know how to fix this or know what the problem is?

As far as I know, the correct way to iterate over an array is through a each block helper
Your template would be written as
<script type="text/x-handlebars" id="people-template">
First loop:<br />
<ul>
{{#each .}}
<li>{{name}}</li>
{{/each}}
</ul>
Second loop:<br />
<ul>
{{#each .}}
<li>{{name}}</li>
{{/each}}
</ul>
</script>
An updated Fiddle http://jsfiddle.net/nikoshr/G83Pk/1/

<div id="placeholder"></div>
<script id="people-template" type="text/x-handlebars">
First loop:<br />
<ul>
{{#each context}}
<li>{{name}}</li>
{{/each}}
</ul>
Second loop:<br />
<ul>
{{#each context}}
<li>{{name}}</li>
{{/each}}
</ul>
</script>
<script type="text/javascript">
var template = Handlebars.compile($("#people-template").html());
var json = {
"context": [
{ "name": "John Doe", "location": { "city": "Chicago" } },
{ "name": "Jane Doe", "location": { "city": "New York"} }
]
};
var html = template(json);
$('#placeholder').html(html);
</script>
You need to correct your iterator to use the each block helper. And your context variable is invalid input for the each iterator. The above code is the proper way to do what you want.

Not sure with the comments, but I encountered very strange behavior while having similar kind of scenario in my code.
{{#with xxxxx}}
{{#each models}}
{{#with attributes}}
{{value}} ---- Worked here
{{/with}}
{{/each}}
{{#each models}}
{{#with attributes}}
{{value}} ---- Didn't Worked here
{{/with}}
{{/each}}
{{/with}}
It worked with first loop but didnt worked with second loop. I did all scenarios at the ended with some strange solution. If I add any Html script or comment at the end of {{#each models}} of second loop, then second loop regains its context and display values properly.
So this worked flawlessly.
{{#with xxxxx}}
{{#each models}}
{{#with attributes}}
{{value}} ---- Worked here
{{/with}}
{{/each}}
{{#each models}} {{! Comment added }}
{{#with attributes}}
{{value}} ---- It works now.
{{/with}}
{{/each}}
{{/with}}

Related

Handlebars - split string and iterate within {{each}}

Basically I am rendering a JSON file into a HTML template with Handlebars and all works fine except for one value where I get returned a string with comma separated values.
JSON file:
[
{
"venue_state": "Everywhere",
"venue_country": "United States",
"venue_lat": "34.2347",
"venue_lng": "-77.9482",
"rating_score": "4",
"created_at": "2022-07-01 17:13:16",
"flavor_profiles": "malty,biscuity,grainy,clove,banana"
},
{
....
}
]
HTML
<script src="https://code.jquery.com/jquery-3.6.0.slim.min.js" integrity="sha256-u7e5khyithlIdTpu22PHhENmPcRdFiHRjhAuHcs05RI=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/handlebars#latest/dist/handlebars.js"></script>
<script src="script.js"></script>
<!--handlebars template-->
<script id="checkin-template" type="text/x-handlebars-template">
{{#each this}}
<div class="container">
<h1>{{venue_country}}<h2>
<p>{{venue_state}}</p>
<div class="some-other-container">
<h1>{{rating_score}}</h1>
</div>
<ul class="tags">
<li>{{flavor_profiles}}</li>
</ul>
</div>
{{/each}}
</script>
<div class="output"></div>
script.js
$(document).ready(function () {
var request = new XMLHttpRequest();
request.open("GET", "testdata.json", false);
request.send(null);
var data = JSON.parse(request.responseText);
//console.log(data);
//compile template and fill with data
var source = $("#checkin-template").html();
var template = Handlebars.compile(source);
$('.output').append(template(data));
});
This works except for {{flavor_profiles}}. What I want to get rendered is this
<ul class="tags">
<li>malty</li>
<li>biscuity</li>
<li>grainy</li>
<li>clove</li>
<li>banana</li>
</ul>
What I actually get is
<ul class="tags">
<li>malty,biscuity,grainy,clove,banana</li>
</ul>
and I guess it is because I get returned a string of comma separated values instead of an array?
I tried something like this
<script id="checkin-template" type="text/x-handlebars-template">
{{#each this}}
<div class="container">
<h1>{{venue_country}}<h2>
<p>{{venue_state}}</p>
<div class="some-other-container">
<h1>{{rating_score}}</h1>
</div>
<ul class="tags">
{{#each flavor_profiles}}
<li>{{this}}</li>
{{/each}}
</ul>
</div>
{{/each}}
</script>
and I also tried to register some helper function in script.js which did not work. This is a small hobby thing where I am just visualizing something in another way. So I don't want to use further frameworks or server side processing. The JSON gets pulled from elsewhere so I can't change how it looks. Is there any way within Handlebars to render my desired output?
I solved it like this
script.js
var data = JSON.parse(request.responseText, function(key, x) {
if (key === "flavor_profiles") {
x = x.split(',');
return x;
}
return x;
});
HTML
<ul class="tags">
{{#each flavor_profiles}}
<li>{{this}}</li>
{{/each}}
</ul>
I think little bit of j Query can help with your problem. I don't know this is what you expected.
HTML
<script id="checkin-template" type="text/x-handlebars-template">
{{#each this}}
<div class="container">
<h1>{{venue_country}}<h2>
<p>{{venue_state}}</p>
<div class="some-other-container">
<h1>{{rating_score}}</h1>
</div>
<ul class="tags" id="stringToList">
{{flavor_profiles}}
</ul>
</div>
{{/each}}
</script>
<div class="output"></div>
and new function to script.js
$(document).ready(function () {
$("[id='stringToList']").each(function () {
let el = $(this).text().toString().split(",").map(item => `<li>${item}</li>`).join('')
$(this).html(el)
})
});

List all keys and values of blaze object

I have inserted this data in mongo
db.orders.insert( { _id: ObjectId().str, name: "admin", status: "online",catalog : [
{
"objectid" : ObjectId().str,
"message" : "sold",
"status" : "open"
}
]})
and i am accessing the data this way
<template name="Listed">
<div class="row">
{{#each list}}
<article class="post">
<h3>{{_id}}</h3>
<h3>{{name}}</h3>
<br>
<h3>{{status}}</h3>
<br>
{{#each catalog }}
<h3></h3>
<h3>{{status}}</h3>
{{/each}}
<div class="well"></div>
<br/>
</article>
<br/><br/>
{{/each}}
</div>
</template>
I am interested in knowing the key/value pair of the catalog object.
The reason for this,is because,i do not know the fields catalog has. For that,i registered a helper
Template.registerHelper("keyval",function(object){
return _.map(object, function(value, key) {
return {
key: key,
value: value
};
});
});
and used it this way
<template name="Listed">
<div class="row">
{{#each list}}
<article class="post">
<h3>{{_id}}</h3>
<h3>{{name}}</h3>
<br>
<h3>{{status}}</h3>
<br>
{{#each keyval catalog }}
<h3></h3>
<h3>{{key}}</h3>
<h3>{{value}}</h3>
{{/each}}
<div class="well"></div>
<br/>
</article>
<br/><br/>
{{/each}}
</div>
</template>
When i try to access the key like {{key}} i get 0,1,2... and {{value}} gives an object.
That is not what am looking for. How can i display the key value pairs correctly?.
You are producing an array of arrays (each catalog item maps to a list of key/value pairs). One solution is to iterate over each of the catalog items and then call keyval on it. The structure would look something like this:
{{#each item in catalog}}
{{#each keyval item}}
<h3>{{key}}</h3>
<h3>{{value}}</h3>
{{/each}}
{{/each}}

Handlebars custom helper error: "options.fn is not a function"

I'm pretty new to handlebars.js and I tried writing a simple if/else helper.
I used this codepen as a guideline.
I already found out that you shouldn't use the # in front of a custom helper but I can't figure out why i'm still getting this error.
This is my index.html :
<html>
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js">
</script>
<script type="text/javascript" src="
https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-beta1/jquery.js"></script>
<script src="data.js"></script>
<script src="helper.js"></script>
</head>
<body>
<script id="entry-template" type="text/x-handlebars-template">
<ul class="test">
{{#each people}}
{{ifThird #index}}
<li>
{{firstName}}
</li>
{{else}}
<li>
{{firstName}}{{lastName}}
</li>
{{/each}}
</ul>
</div>
</div>
</script>
</body>
</html>
... and this is data.js :
$(function(){
var templateScript = $("#entry-template").html();
var theTemplate = Handlebars.compile(templateScript);
var context = {
people: [
{firstName: "Yehuda", lastName: "Katz"},
{firstName: "Carl", lastName: "Lerche"},
{firstName: "Alan", lastName: "Johnson"},
{firstName: "Dik", lastName:"Neok"},
{firstName: "Bob", lastName:"van Dam"},
{firstName: "Erik", lastName: "Leria"}
]
};
var html = theTemplate(context);
$('.test').html(html);
$(document.body).append(html);
})
... and this is helper.js (the error should be in here) :
Handlebars.registerHelper('ifThird', function (index, options) {
if(index%3 == 0){
return options.fn(this);
} else {
return options.inverse(this);
}
});
You need to edit your template to:
<ul class="test">
{{#each people}}
{{#ifThird #index}}
<li>
{{firstName}}
</li>
{{else}}
<li>
{{firstName}}{{lastName}}
</li>
{{/ifThird}}
{{/each}}
</ul>
You need to start the block with {{#ifThird}} and close the block with {{/ifThird}}.

for each item in array {{display item}}

I am trying to access the array values in my code so that I can use them but I am not sure how to. Thanks
var footerButtons = ['NO', 'EXTRA', 'YES'];
<template name="footer">
{{#each footerButtons}}
<h1>
<button class="col-xs-2 mainMenu" type="button">{{what should go here?}}</button>
</h1>
{{/each}}
</template>
You could define your footerButtons helper as follows -
Template.footer.helpers({
footerButtons() {
return [{text: 'NO'}, {text: 'EXTRA'}, {text: 'YES'}];
}
});
Then in your template, you can access the values as shown below.
<template name="footer">
{{#each footerButtons}}
<h1> <button class="col-xs-2 mainMenu" type="button">{{text}}</button> </h1>
{{/each}}
</template>
{{.}} or {{this}} is what you're looking for. This refers to the current object/element in an array.

Iterating over nested each statements in meteorjs (group by category)

I have a list of products, each of which has a category associated with it.
I am trying to list out the products grouped by category similar to this example but I cannot get it it to work. It just shows up a unique list of the categories but not the products
I have the following helper:
Template.supplierPage.helpers({
getCategories: function(){
var categories = Products.find({supplierId: this._id},
{sort:{category: 1}, fields: {category: 1}}).fetch();
return _.uniq( categories, true, function (product){
return product.category;
});
},
products: function(category) {
return Products.find({category: category, supplierId: this._id});
}
});
And the following template:
<template name="supplierPage">
<div class="ui dividing header">
<h3 class="ui header">My Products</h3>
</div>
<div>
{{> productCreate}}
{{#each getCategories}}
<h1>{{category}}</h1>
{{#each products.category}}
{{> productItem}}
{{/each}}
{{/each}}
</div>
Any help would be greatly appreciated. Thanks.
You almost got it right. You are just not providing the argument to the products helper in the right way. This should work:
<template name="supplierPage">
<div class="ui dividing header">
<h3 class="ui header">My Products</h3>
</div>
<div>
{{> productCreate}}
{{#each getCategories}}
<h1>{{category}}</h1>
{{#each products category ../_id}}
{{> productItem}}
{{/each}}
{{/each}}
</div>
You also need to fix your use of this in the products helper, because it changes when using #each. The easiest is to provide it as an additional argument as above:
products: function(category, supplier) {
return Products.find({category: category, supplierId: supplier});
}

Categories