I want to use handlebars #each with an object that's not an array.
How do I do that? I need it to still work with meteor's special features with #each.
My object is in the form of:
{
john: "hello",
bob: "hi there"
}
I'm trying to get an output like this:
<div>hello</div>
<div>hi there</div>
You need to use a helper in your js to help handlebars understand your object:
Add to your client js
Template.registerHelper('arrayify',function(obj){
var result = [];
for (var key in obj) result.push({name:key,value:obj[key]});
return result;
});
And use (you can also use the key with {{name}}) in your html:
{{#each arrayify myobject}}
<div title="hover here {{name}}">{{value}}</div>
{{/each}}
myobject comes from your template:
Template.templatename.helpers({
myobject : function() {
return { john:"hello", bob: "hi there" }
}
});
You can convert your object into array with underscore _.map
html:
<template name="test">
{{#each person}}
<div>{{greeting}}</div>
{{/each}}
</template>
js:
Template.test.helpers({
person : function () {
return _.map(object, function(val,key){return {name: key, greeting: val}});
}
});
It should be noted for people finding this now that the correct way to declare Handlebars helpers in Meteor as of this writing is with the UI.registerHelper method as opposed to Handlebars.registerHelper. So the above helper should look like this now:
UI.registerHelper("arrayify", function(obj){
result = [];
for (var key in obj){
result.push({name:key,value:obj[key]});
}
return result;
});
Related
I need to generate code from some array,the code should be generate according to the array value
for example,
alert("sofia")
alert("Miley")
alert("lindzy")
...
I've created for that array with the following structure
var customerList = {
cusomer: []
};
and in the customer array there is name:value like name:sofia,name:Miley ....
The template look like
{{#each customerList.customer}}
alert( {{name}} );
{{/each}}
And this is generate the Html with {object,objet},what am I missing here?
The array look like
Try this:
customerList['cusomer'].forEach(function(e){
alert(e.name)
});
I have js function which returns hash:
Template.mainmenu.menuitem = function() {
var links = {};
links["mail"] = "http://some.net";
links["rss"] = "http://rss.com";
return links;
};>
I want to iterate over this hash in the HTML template and create set of links.
I has tried this code:
<template name="mainmenu">
{{#each menuitem}}
{{this}}
{{/each}}
</template>
But it return nothing. If I will change hash to array all works fine.
How can I iterate over hash and construct html links:
{{this.key}}
I don't think what handelbars has bult-in helper for iterating over js hash, but you can write your own helper which will return content of the hash or you can to use "with":
{{#with links}}
<p>{{{mail}}}</p>
<p>{{{rss}}}</p>
{{/with}}
As Meteor includes underscore by default, you can use underscore methods to extract contents of the hash:
Template.mainmenu.menuitem = function() {
var links = {};
links["mail"] = "http://some.net";
links["rss"] = "http://rss.com";
return _.values(links);
};
To continue where Hubert OG left off:
Template.mainmenu.menuitem = function() {
var links = {};
links["mail"] = "http://some.net";
links["rss"] = "http://rss.com";
return _.map(links, function (value, key) {return {_id: key, key: key, value: value}});
};
It's important to have an _id field as well so that Meteor's Spark rendering engine better understands what to redraw when things change.
I have a json object in collection which I need to show it on the page.
Here is what I did:
I first call the helpers template then in that I fetch the json object from the collection:
I am using coffeescirpt and jade-handlebars, here goes my code in coffeescript:
Template.test.helpers
test: ->
test = Question.find().fetch();
test
In the console when I do Question.find().fetch() the following thing occurs:
QuestionData: Object
question1: "How many kids do I have ?"
question2: "when will i die ?"
question3: "how many wife do i have ?"
question4: "test"
__proto__: Object
_id: "w9mGrv7LYNJpQyCgL"
userid: "ntBgqed5MWDQWY4xt"
specialNote: "Rohan ale"
Now in the jade when I call the template by:
template(name="hello")
.test {{QuestionData}}
I can see only the [object] [object]. To see the question1,question2 I have to do the following:
template(name="hello")
.test {{QuestionData.question1}}, {{QuestionData.question2}}
How can I dynamically show all the questions without doing {{QuestionData.question1}} ...
Thank You in advance !!!
You can dynamically compose field names in a loop.
b = { q1: 'a1', q2: 'a2', q3: 'a3' };
for (x=1;x<=3;x++) { console.log( b[ 'q' + x ] ) }
That being said, there's a lot here that seems a misunderstanding to me. I'd step back and say that you should look into storing one question per mongo document. This gives you the easiest data for meteor to play with. Or, storing multiple questions in an array:
test = {
questions : [
"How many kids do I have ?"
"when will i die ?"
"how many wife do i have ?"
"test" ] ,
userid: "ntBgqed5MWDQWY4xt",
specialNote: "Rohan ale"
}
The problems come when you think how you store the answers, sort the questions, etc. Probably a collection called questions, with a field maybe called sortOrder, a field called tag, etc.
How did you pick up calling templates this way, rather than having them as html files that a router manages for you?
instead of just returning your json object with Questions.find().fetch() you could add another step to put your data into an array like:
test = function() {
var temp = [];
for (item in Questions.find().fetch()) {
temp.push(item);
};
return temp;
};
return test;
(sorry for not writing in coffee script, i'm not aware of the language abstraction)
To answer your question on how to do it, you can do something like this(in JS, sorry, not a coffeeScripter):
Template.Questionnaire.questions = function () {
var questions = [];
_.each(Object.keys(this), function (field) {
if(/^question.+/.test(field)) {
questions.push({ label: field, question: this[field]});
}
});
return questions;
};
And then in a template:
<template name="Questionnaire">
{{#each questions}}
<label>{{label}}</label>
<span>{{question}}</span>
{{/each}}
</template>
Something like that. But I agree with Jim Mack and that you should probably be storing this in an array.
Like as JIm Mack Posted, save your collection in an array
first of all insert your question in an array by doing these in your coffeescript:
x = document.getElementById('question-form')
length = x.length
i = 0
question = []
while i< length
aaa = x.elements[i]
question.push
questions: aaa
i++
then since you are using Jade-handlebars you need register helpers
in your jade file do these
{{#each arrayify myObject}}
{{#each this.name}}
p {{questions}}
{{/each}}
{{/each}}
The arrayify and myObject are the handlebars helpers. Then in your coffeescript
Handlebars.registerHelper "arrayify", (obj) ->
result = []
for temp in obj
userQuestion = temp.question
result.push
name: userQuestion
return result
Template.templatename.myObject = ->
temp = []
for item in Question.find().fetch()
temp.push item
return temp
Hope these will work.
I tried figuring this out by reading both how the Tags are handled in the Todo-List Example and how the RSVPS are handled in the Parties example, but I could not figure out a general way to achieve my Goal.
I have a Plan Collection which has a name ownerid and excerciselist
Plans.insert({name: names[i], ownerId: 1, excerciselist: excercises});
Now in this Excerciselist, I want to add an undefined Amout of Excercises by ID.
I have an Excercisecollection with the following:
Excercises.insert({name: names[i], muscle: muscles[i], device: devices[i], description: descriptions[i]});
Now all these Excercises have a unique _id element by default.
Adding things to the excerciselist no Problem I can do that by Push.
But what I can not figure out is, how I can access only certain ID's in the excerciselist via it's Index.
I can only access the whole Stringarray and output in in HTML via
{{#each planarray}}
{{excerciselist}}
{{/each}}
But there is no possiblity to do smoething like
{{ excerciselist }}
I have also tried returning only excerciselist to the planarray, but the problem is that because it is only indexed and not mapped it can not be accessed by the LiveHTML.
Does anyone have an Idea how this problem could be solved?
Why don't you add a field for the unique id to the Excersies insert?
Excercises.insert({ uniqueID: [i], name: names[i], muscle: muscles[i], device: devices[i], description: descriptions[i]});
This way you can get just the excercise you want based on the uniqueID-field.
Oh and you should probably call "uniqueID" something that makes more sense.
I found a little Workaround which is not exactly what I had in mind but it gets the job done.
Template.editplan.excercises = function() {
var names = [];
var add = [];
var adder = null;
for(var i = 0; i < this.excerciselist.length; i++)
{
add[i] = [];
adder = Excercises.find({_id: this.excerciselist[i]});
add[i]['_id'] = this.excerciselist[i];
add[i]['location'] = i;
adder.forEach(function (obj) {
add[i]['name'] = obj.name;
});
names.push(add[i]);
}
return names;
};
Basically I made a new Array in which i put the Data I want to have so I can read it in the LiveHTML see example below
{{#each planarray}}
<h1>{{name}}</h1>
{{#each excercises}}
{{name}}
{{/each}}
<select name="selectedexcercise{{_id}}" id="selectedexcercise{{_id}}">
{{> excerciseoption}}
</select>
<input type="button" class="addtoplan" value="Eine Übung hinzfügen">
{{/each}}
But there must be a more efficient or nice way.... At least I hope so!
I'm using Handlebars templates and JSON data is already represented in [Object object], how do I parse this data outside of the Handlebars? For example, I'm trying to populate a JavaScript variable on the page through a handlebars tag, but this doesn't work.
Any suggestions? Thank you!
EDIT:
To clarify, I'm using ExpressJS w/ Handlebars for templating. In my route, I have this:
var user = {}
user = {'id' : 123, 'name' : 'First Name'}
res.render('index', {user : user});
Then in my index.hbs template, I now have a {{user}} object. I can use {{#each}} to iterate through the object just fine. However, I'm also using Backbonejs and I want to pass this data to a View, such as this:
myView = new myView({
user : {{user}}
});
The problem is that {{user}} simply shows [Object object] in the source. If I put it in console.log I get an error that says 'Unexpected Identifier'.
When outputting {{user}}, Handlebars will first retrieve the user's .toString() value. For plain Objects, the default result of this is the "[object Object]" you're seeing.
To get something more useful, you'll either want to display a specific property of the object:
{{user.id}}
{{user.name}}
Or, you can use/define a helper to format the object differently:
Handlebars.registerHelper('json', function(context) {
return JSON.stringify(context);
});
myView = new myView({
user : {{{json user}}} // note triple brackets to disable HTML encoding
});
You can simple stringify the JSON:
var user = {}
user = {'id' : 123, 'name' : 'First Name'};
// for print
user.stringify = JSON.stringify(user);
Then in template print by:
{{{user.stringify}}};
I'm using server-side templating in node-js, but this may apply client-side as well. I register Jonathan's json helper in node. In my handler, I add context (such as addressBook) via res.locals. Then I can store the context variable client-side as follows:
<script>
{{#if addressBook}}
console.log("addressBook:", {{{json addressBook}}});
window.addressBook = {{{json addressBook}}};
{{/if}}
</script>
Note the triple curlies (as pointed out by Jim Liu).
You are trying to pass templating syntax {{ }} inside a JSON object which is not valid.
You may need to do this instead:
myView = new myView({ user : user });
In the Node Router - Stringify the response object. See below line.
response.render("view", {responseObject:JSON.stringify(object)});
In HTML Script tag - user Template literals (Template strings) and use JSON.parse.
const json= `{{{responseObject}}}`;
const str = JSON.parse(json);
Worked like a charm!
You can render the keys/values of a list or object in a Handlebars template like this:
{{#each the_object}}
{{#key}}: {{this}}
{{/each}}
If you want more control over the output formatting you can write your own helper. This one has a recursive function to traverse nested objects.
Handlebars.registerHelper('objToList', function(context) {
function toList(obj, indent) {
var res=""
for (var k in obj) {
if (obj[k] instanceof Object) {
res=res+k+"\n"+toList(obj[k], (" " + indent)) ;
}
else{
res=res+indent+k+" : "+obj[k]+"\n";
}
}
return res;
}
return toList(context,"");
});
We used handlebars for email templates and this proved useful for a user like the following
{
"user": {
"id": 123,
"name": "First Name",
"account": {
"bank": "Wells Fargo",
"sort code": " 123-456"
}
}
}
To condense (what I found to be) the most helpful answers...
JSON helper for handlebars (credit):
Handlebars.registerHelper("json", function (context) {
return JSON.stringify(context);
});
JSON helper for express-handlebars (credit and I updated to newest conventions):
app.engine(
"handlebars",
exphbs.engine({
defaultLayout: "main",
helpers: {
json: function (context) {
return JSON.stringify(context);
}
}
})
);
And then on the templating side: {{json example}}
Just improving the answer from #sajjad.
Adding a 'pre' tag will make it look a lot nicer.
<pre>
{{#each the_object}}
{{#key}}: {{this}}
{{/each}}
</pre>