Handlebars.js is returning an empty template - javascript

I am running a node server with Koa and using Handlebars for templating. I have the following template:
<script id="side_item" type="text/x-handlebars-template">
<li>
<i class="{{this.icon}}"></i> {{this.name}}
</li>
</script>
And my javascript code is:
var source = $("#side_item").html();
var template = Handlebars.compile(source);
var html = template({
route: "museum",
icon: "fa fa-institution fa-fw",
name: "Museum"
});
But I am getting the following source:
<script id="side_item" type="text/x-handlebars-template">
<li>
<i class=""></i>
</li>
</script>
I assumed the script tags would be removed and the completed template would be rendered. Is it possible the brackets for the expressions are removed before handlebars can see them? Or is there something wrong with how I am compiling the template? I am following the examples at the handlebars website as closely as I can.

Related

javascript get value of Mongo field already rendered - Meteor

Hey everyone, thank you very much for your help. Question is edited per suggestions in the comments.
I'm new to Mongo and Meteor.
I have a collection "posts" with a field "slug".
The "post" template is populating correctly with each post's values. Slug value is always something like "my-great-post".
I need to get the text value for the _id's slug, which will be different each time the template is accessed, encode it, write a string, and spit the string back out into the template.
Things tried
can't return a value for "this.slug" or "this.data.slug" in either template helpers or onRendered, even though collection is defined and correctly populating spacebars values in the template
"this" returns "[object Object]" to console.log
app crashes when I try to javascript encode and deliver a string from the helper, probably I don't fully understand helper syntax from the documentation
(I followed advice in the comments to avoid trying to create scripts in the template html, so below is more information requested by everyone helping on this thread)
- Template html -
{{#with post}}
<div class="blog-article">
<div class="blog-header">
<div class="left">
<!-- title -->
<h1 class="post-title">{{title}}</h1>
<div class="holder">
<div class="post-tags">
<!-- tags -->
{{#each tags}}
<span>{{this}}</span>
{{/each}}
</div>
</div>
</div>
</div>
<div class="blog-post">
<div class="blog-copy">
<!-- date -->
<div class="post-date">{{post_date}}</div>
<!-- social -->
<div class="blog-social">
<!--
<a class="so-facebook" target="_blank" href="need to encode slug here"></a>
-->
</div>
<!-- ============== post ============== -->
{{{content}}}
<!-- ============ end post ============ -->
</div>
</div>
</div>
{{/with}}
- Template js -
Template.post.onCreated(function() {
var self = this;
self.autorun(function() {
var postSlug = FlowRouter.getParam('postSlug');
self.subscribe('singlePost', postSlug);
});
});
Template.post.helpers({
post: function() {
var postSlug = FlowRouter.getParam('postSlug');
var post = Posts.findOne({slug: postSlug}) || {};
return post;
}
// can't get these working in a helper, out of helper they crash the app
// console.log(this.slug);
// console.log(this.data.slug);
});
Template.post.onRendered( function () {
// these do not work
// console.log(this.slug);
// console.log(this.data.slug);
});
db.posts.findOne();
{
"_id" : ObjectId("576c95708056bea3bc25a91f"),
"title" : "How Meteor Raised the Bar For New Rapid-Development Technologies",
"post_date" : "May 28, 2016",
"image" : "meteor-raised-the-bar.png",
"slug" : "how-meteor-raised-the-bar",
"bitlink" : "ufw-29Z9h7s",
"tags" : [
"Tools",
"Technologies"
],
"excerpt" : "sizzling excerpt",
"content" : "bunch of post content html"
}
If some one can solve this using any method, I will accept answer with joy and gratitude most intense.
The problem is probably with the parent template, rather than this one. The way that Meteor works is that the JS files are separated from the HTML, so don't try to include a <script> tag in the HTML.
The first thing is that you have to load all of your documents into the client. (NOTE: once you've got the hang of that, then you can worry about only loading the documents that you need).
To do that, you need a collection and a publication. By default all collections are automatically published completely, so unless you removed the autopublished module, then I'll assume that it is still loaded.
So let's start with the parent template. In this case, I'm going to just loop through all of the posts in the collection and display them using the innerTemplate.
<template name=parent>
<ul>
{{#each post}}
{{> innerTemplate}}
{{/each}}
</ul>
</template>
And now our inner template might look like this:
<template name=innerTemplate>
<li>{{slug}}</li>
</template>
The end result will be a simple list with each slug.
Now, to link everything together, we need to create a JS file, which will:
1. define the collection on both client and server
2. pass the collection to the parent template
This file should be accessible to both the client and the server.
posts = new Mongo.Collection('posts');
if(Meteor.isClient) {
Template.parent.helpers({
posts() {
return Posts.find();
}
});
}
Now, if you want to do something with 'slug' in the JS file, you could do something like this:
if(Meteor.isClient) {
Template.innerTemplate.helpers({
upperCaseSlug() {
return this.slug.toUpperCase();
}
});
}
Then, you could refer to upperCaseSlug in your template, like thus:
<template name=innerTemplate>
<li>{{upperCaseSlug}}</li>
</template>
A few things about Meteor:
You should never see a pattern such as:
<script type="text/javascript">
...some code
</script>
Because Meteor combines all your js files into one big file and includes it automatically in your app. You should never have to declare your own script in this way.
Secondly, you should never have to get the value of a data object by reading the DOM. The data context of each template gives you your data in the variable this.
In either a helper or template event you can refer to this and be assured that you're going to get exactly the data being displayed in that instance of the template.
Having now seen your template code it's now apparent that your template has no data context - you set the data context inside your {{#with post}} and its associated helper but that doesn't end up creating the this you need one level below.
So... #Nathan was on the right track except that he assumed you were iterating over a cursor instead of just looking at a single post.
Take all html you have between your {{#with post}} and {{/with}} and put it in a new template, say postDetail then make your outer template:
<template name="post">
{{#with post}}
{{> postDetail}}
{{/with}}
</template>
Now your postDetail template will get a data context equal to the post object automatically and your helpers can refer to this safely.
Template.postDetail.helper({
slugURI{
return "/"+encodeURI(this.slug);
}
});
Then in your postDetail template you can get the encoded slug with:
<a class="so-facebook" target="_blank" href={{slugURI}}>

Handlebars template not filling in context

I'm building a Flask application and have some client-side HTML rendering which I would like to do using Handlebars templates.
I have the following very basic handlebars template:
<script id="source-template" type="text/x-handlebars-template">
<div class="source-data">
<span>{{last4}}</span>
<span>Exp : {{exp_month}} / {{exp_year}}</span>
</div>
</script>
And I am using it as follows (exactly as in the handlebars tutorial):
var source = $('#source-template').html();
var template = Handlebars.compile(source);
var context ={
last4:'1234',
exp_month:'12',
exp_year:'2020'
};
var html = template(context);
console.log(html);
However it doesn't seem to be inserting the data from my context into the template. The console output is:
<div class="source-data">
<span></span>
<span>Exp : / </span>
</div>
Am I missing something here? I'm not sure what could be going wrong, as I essentially copied the handlebars example.
Since I am using Flask, I had the script written in an html file which was rendered by Jinja.
Because there were no variables named last4, exp_month, or exp_year passed into Flask's render_template() function, it replaced them with nothing, leaving the handlebars template with no variables.
The solution for this was to use {% raw %} around the script so that Jinja wouldn't interpret these as variables:
<script id="source-template" type="text/x-handlebars-template">
{% raw %}
<div class="source-data">
<span>{{last4}}</span>
<span>Exp : {{exp_month}} / {{exp_year}}</span>
</div>
{% endraw %}
</script>

Render an embedded partial template from a pre-compiled template using handlebars?

I have a pre-compiled handlebars template that is rendering as expected.
I've decided to add an embedded (not pre-compiled) partial reference to the template that I'm pre-compiling but the content of the partial is not rendering properly.
The end result is that the contents of the embedded partial are included in the rendered HTML (I suppose my partial is "partially" working :-D ), but:
the name of the partial is also included on each line
the expression in the partial is not populated with my data
This is the HTML that is rendered. The 3 lines from the partial named templateBodyItemNumberPartial are listed below (the partial name seems to be prefixed to each line of output) and no data is rendered in the <span>:
<div class="templateBodyItem">
templateBodyItemNumberPartial
templateBodyItemNumberPartial <span class="templateBodyItemNumber"></span>
templateBodyItemNumberPartial
this is my body text
<div class="templateBodyItemFooter">this is my footer text</div>
</div>
This is the main, pre-compiled template that references the templateBodyItemNumberPartial partial:
{{! templates/nameWithPartial.handlebars }}
{{! precompiled via: handlebars ./templates/nameWithPartial.handlebars -f external_template_with_partial.js }}
<div class="templateBodyItem">
{{> templateBodyItemNumberPartial }}
{{ templateBodyItem }}
<div class="templateBodyItemFooter">{{ templateBodyFooter }}</div>
</div>
This is the .html file where I define/register the partial and invoke my pre-compiled nameWithPartial template:
<script type="text/javascript" src="./handlebars-v2.0.0-alpha.4.js"></script>
<script type="text/javascript" src="./external_template_with_partial.js"></script>
<script id="templateBodyItemNumberPartial" type="text/x-handlebars-template">
<span class="templateBodyItemNumber">{{ templateBodyItemNumber }}</span>
</script>
var templateBodyObject = {
templateBodyItemNumber : 4 ,
templateBodyItem : 'this is my body text' ,
templateBodyFooter : 'this is my footer text'
};
// verify that the partial html is what I think it should be
console.log( $( '#templateBodyItemNumberPartial' ).html() );
//=> <span class="templateBodyItemNumber">{{ templateBodyItemNumber }}</span>
Handlebars.registerPartial( 'templateBodyItemNumberPartial' , $( '#templateBodyItemNumberPartial' ).html() );
var templateHtml = Handlebars.templates.nameWithPartial( templateBodyObject );
I'm attempting to use an embedded partial just to test how partials work in handlebars.
Can anyone confirm if this functionality is supported OR do I have to use pre-compiled partials if my main template that references the partial is pre-compiled?
Many thanks!
BTW, I'm running Win7 Pro 64-bit but hopefully that's not the issue...
This issue seems to be solved by rolling back handlebars to #1.3.0.
I changed one line of code above from:
<script type="text/javascript" src="./handlebars-v2.0.0-alpha.4.js"></script>
to:
<script type="text/javascript" src="./handlebars-v1.3.0.js"></script>
Then I rolled back the handlebars CLI to #1.3.0:
$ npm install -g handlebars#1.3.0
and re-compiled the external template:
$ handlebars ./templates/name_with_partial.handlebars -f external_template_with_partial.js
I'm guessing there is either an issue with partials and #v2.0.0-alpha.4 OR the partials functionality in this latest version has changed. I deserve what I get for using something with "alpha" in the name. :)
Oh well, thanks for listening.

Handlebarsjs: render multiple passes with nested templates

I see many things that just refers me to partials which sucks because they have to built out of the layout context.
What I'm wanting to do is make nested templates
For example:
<div id="person">
{{name}}
<div id="address">
{{street}}
</div>
</div>
<script>
var outer = Handlebars.compile($('#person').html());
outer({ name: 'someone special' });
var inner = Handlebars.compile($('#address').html());
inner({ street: 'somewhere cool' });
</script>
When running this, the inner template is never rendered as the outer templating gobbles it up.
It would be nice if you could namespace nested templates like this:
{{> name}}
<div id="person">
{{name}}
{{> address}}
<div id="address">
{{street}}
</div>
{{/> address}}
</div>
{{/> name}}
<script>
var outer = Handlebars.compile($('#person').html(), 'name');
outer({ name: 'someone special' });
var inner = Handlebars.compile($('#address').html(), 'address');
inner({ street: 'somewhere cool' });
</script>
or something like this, so that when the outer renders, it will leave the address alone and let inner render address itself without removing it from the DOM.
Is anything like this possible?
The reason for this question is that I'm using backbone and want to separate out all of my small views but it compiles to one file. when the outer is templated with handlebars, everything else breaks. I don't want to use partials as that just take everything out of the flow of the html document for the designers.
EDIT
I think what I really need is a way to do {{noparse}} and from the registerHelper just return the raw html between the noparse tags
Here is a demo of a no-parse helper. To use this functionality, you will need to use a version of at least v2.0.0-alpha.1. You can get it from the handlebars build page. Here is the pull request that details about it.
Here is the relevant code.
Template
<script id="template" type="text/x-handlebars-template">
<div id="person">
{{name}}
{{{{no-parse}}}}
<div id="address">
{{street}}
</div>
{{{{/no-parse}}}}
</div>
</script>
Handlebars.registerHelper('no-parse', function(options) {
return options.fn();
});
You're missing a crucial step. You have to take the template & compile it (which you're doing) but you also have to register the partial.
Really the pattern you want to use is the partial template - where you register the inner template as a partial template then fill it with the person's address data - there is a great example here, but this also will help in your specific HTML setup.
<script id="person-template" type="text/x-handlebars-template">
{{#each person}}
{{name}}
{{> address}}
{{/each}}
</script>
<script id="address-partial" type="text/x-handlebars-template">
<div class="address">
<h2>{{street}}</h2>
</div>
</script>
<script type="text/javascript">
$(document).ready(function() {
var template = Handlebars.compile($("#person-template").html());
Handlebars.registerPartial("address", $("#address-partial").html());
template({ name: 'someone special', street: 'somewhere cool' });
}
</script>

handlebars partial context

I have an array that contains the information for social buttons (href,alt,img). I created a partial that would cycle through the array and add the objects, here is what I have.
the array:
var social=[
{
"url":"http://twitter.com/share?text="+encodeURIComponent(this.title)+" "+encodeURIComponent('#grnsrve')+"&url="+domain+"&via="+twitterAcct,
"image":"IMG/media/twitter_16.png",
"alt":"twitter link"
},
{
"url":"http://twitter.com/share?text="+encodeURIComponent(this.title)+" "+encodeURIComponent('#grnsrve')+"&url="+domain+"&via="+twitterAcct,
"image":"IMG/media/twitter_16.png",
"alt":"twitter link"
},
{
"url":"http://twitter.com/share?text="+encodeURIComponent(this.title)+" "+encodeURIComponent('#grnsrve')+"&url="+domain+"&via="+twitterAcct,
"image":"IMG/media/twitter_16.png",
"alt":"twitter link"
}
];
The template:
social_partial = '<img src="{{image}}"/>';
The partial function:
Handlebars.registerPartial('addSocial',social_partial);
and the main template:
<div class="tip_social">
{{>addSocial social}}
</div>
I'm getting a 'Depth0 is undefined error'. I tried looking for documentation on parials getting a different context , but I have yet to find it.
edit here is a more complete fiddle of it
Same issue for me. Bit of digging and one face-palm later here's what I've found.
tl;dr answer
Be very careful about the context you pass to your partial. If you somehow pass a null, or empty object to your partial you'll get the depth0 is undefined error
Very slightly longer answer
JSFiddle Examples:
depth0 bug gets triggered http://jsfiddle.net/paulcollinsiii/dAmfc/
working version http://jsfiddle.net/paulcollinsiii/sMAkT/
The only thing that changed in the working version is I'm passing a valid context to the partial.
BTW a very helpful trick is to use the debug helper from this blog
Answerbot dictator "code example"
<script id="parent">
<table>
<thead>
<tr>
<th>One</th>
<th>Two</th>
</tr>
</thead>
<tbody>
{{#each collection}}
{{>myPartial nothingHere}} <!-- Broken context -->
{{/each}}
</tbody>
</table>
</script>
The broken context above will cause handlebars to die. For your problem try digging into that with a {{debug}} tag and see if that helps.
For a better code example please just take a look at the jsFiddles. Reposting all the code in here, formatting it to make it pretty looking and making the StackOverflow answerbot dictator happy was a bit much for doing this at work ^_~
I'm not sure what you're doing wrong as you haven't provided enough code to duplicate your problem. However, it isn't hard to make it work. First of all, your main template should be iterating over social and feeding each element to your partial, something like this:
<script id="t" type="text/x-handlebars-template">
{{#each social}}
<div class="tip_social">
{{>addSocial this}}
</div>
{{/each}}
</script>
And then you can get your HTML with something like this:
​var t = Handlebars.compile($('#t').html());
var html = t({social: social}));​​
Demo: http://jsfiddle.net/ambiguous/SsdbU/1/
Here is the only way I found:
Handlebars doc indicates that you need to use Block Helpers to pass different contexts. So here are two files: the main template and the script in node contening two contexts: the main context and the context for the social data. The script having the source for the partial template. The Block is #twitter_list and its associated registerHelper uses option.fn(other_context_object) which seems the only way to pass another context in handlebars.
Main template:
<html>
<head>
<title>Test content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<div id = main>
<!-- This is using the main context-->
<h2> Hello {{name}}!</h2>
<p>{{some_content}}</p>
<!-- This is using the partial template and its own context -->
<ul>
{{#twitter_list}}
{{>yourpartial}}
{{/twitter_list}}
</ul>
</div>
</body>
</html>
The javascript using Node js:
var handlebars = require('handlebars'),
fs = require('fs');
const main_context = { name: "world",
some_content: "Bla bla bla all my bla bla"};
const twitter_data={social:[
{
"url":"some url 1",
"image":"IMG/media/twitter_1.png",
"alt":"twitter link 1"
},
{
"url":"some url 2",
"image":"IMG/media/twitter_2.png",
"alt":"twitter link 2"
},
{
"url":"some url 3",
"image":"IMG/media/twitter_3.png",
"alt":"twitter link 3"
}
]};
var partial_source = '{{#each social}}<li><img src="{{image}}"/></li>{{/each}}';
handlebars.registerPartial('yourpartial', partial_source);
handlebars.registerHelper('twitter_list', function(options) {
//you need to use the options.fn to pass the specific context
return options.fn(twitter_data);
});
fs.readFile('maintemplate1.hbs', 'utf-8', function(error, source){
var template = handlebars.compile(source);
var html = template(main_context);
console.log(html)
});

Categories

Resources