Handlebars, avoid compiling (ignore) part of a template? - javascript

Is there a way to tell the Handlebar compiler to ignore a block of template.
I know there is the \ solution, like :
\{{ is.ignored}}
but is there something that would do the same, but for a complete block, like :
<script type="text/x-handlebars-template" id="my-template">
<ul>
{{#each items}}
<li>{{display}}</li>
{{/each}}
</ul>
</script>
I believe it would be better (and far more readable) to have something like {{#ignore}}{{/ignore}} instead of adding \ everywhere.
I tried to find something using block helpers, either building something myself, but I can't get my hand on the non-compiled version of what's inside the block.

Unfortunately, Cyril's answer seems out of date? I found this alternative in the Handlebars documentation on raw blocks:
Raw Blocks
Raw blocks are available for templates needing to handle unprocessed mustache blocks.
{{{{raw-helper}}}}
{{bar}}
{{{{/raw-helper}}}}
will execute the helper raw-helper without interpreting the content.
Handlebars.registerHelper('raw-helper', function(options) {
return options.fn();
});
will render
{{bar}}

Yes I finally found it, it's called ... raw ! :
{% raw %}
<script type="text/x-handlebars-template" id="my-template">
<ul>
{{#each items}}
<li>{{display}}</li>
{{/each}}
</ul>
</script>
{% endraw %}
Update : After an update of Handlebars, this snipped seems to not work now. I opened a ticket to see how to make it works.

Related

{{#each}} makes handlebars template disappear

I have a template inside a script tag
<div class = "exhibitionDescription">
<h3>{{title}}</h3>
<p>{{infoText}}</p> </div>
and the browser renders it fine,
but when I surround it with {{#each arrayName}} {{/each}}
{{#each arrayName}}
<div class = "exhibitionDescription">
<h3>{{title}}</h3>
<p>{{infoText}}</p> </div>
{{/each}}
browser renders the script tag absolutely blank.
The handlebars library is included before the script tag
(here is a screenshot of the final browser output)
Here is the same code, but without the {{#each}} {{/each}} around the template
UPD! Turns out {{#each}} tag "cuts" everything it surrounds, including itself, which is not something is that supposed to happen...
UPD 2! I think i've got the problem - double curly brackets ({{ }}) just make the code in between disappear! Why tho?
I solved it - I was using handlebars to insert the templates themselves, that's why my curly braces were interpreted by handlebars as data inputs, and it just removed them when rendering!
Instead of
{{text}}
, I should have used the
\{{text}}
, which fixes the problem and makes it leave the curly braces alone.

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}}>

What does {{^ mean in handlebars

I have a handlebars template that contains:
{{^is mymodel.someproperty}}
I don't understand what the significance of the caret symbol is. I've searched around, the only place I'm seeing it is on Handlebars Expressions
It's used like so:
{{#each nav}}
<a href="{{url}}">
{{#if test}}
{{title}}
{{^}}
Empty
{{/if}}
</a>
{{~/each}}
What does "{{^" mean in handlebars? It sort of looks like a .NOT. or .ELSE. or something like that.
-Eric
The reason it's not in the handlebars doc is because it's a mustache construct called an Inverted Section.
See: https://mustache.github.io/mustache.5.html#Inverted-Sections
{{#repo}}
<b>{{name}}</b>
{{/repo}}
{{^repo}}
No repos :(
{{/repo}}
... disabling inverse operations such as {{^foo}}{{/foo}} unless fields are explicitly included in the source object._
http://handlebarsjs.com/reference.html
http://handlebarsjs.com/expressions.html

Sending different data to an embedded component in Ember

I've got a drag and drop setup in two views, currently. I am trying to pull those out and make reusable components out of them. At this point I'm not even worried about the actions and bringing that information back (that will be the next issue that I will look into). Right now I'm only worried about getting the proper data to display.
The templates for my current setup are:
The outside list view:
<ul class="sortable-list">
{{#each view.permission}}
{{ view App.GroupsPermissionView }}
{{/each}}
</ul>
The individual item view:
<li class="sortable-item">{{ this.displayName }}</li>
I have changed this into the following component templates:
drag-and-drop.handlebars:
<ul class="sortable-list">
{{#each dad}}
{{ drag-and-drop-item dadi=this.displayName }}
{{/each}}
</ul>
and drag-and-drop-item.handlebars:
<li class="sortable-item">{{ dadi }}</li>
I call this with {{ drag-and-drop dad=unselectedPermissions }} or {{ drag-and-drop dad=selectedPermissions }}
and this works fine.
Here is my issue. My other use of drag and drop does not use the field name of "displayName". The field is just "name". I'm sure other co-workers will need to also use other field names as well.
I've tried many different things to attempt to get it passed through to the inner component, but nothing has worked. I think it is a limitation in handlebars and that this will be impossible to do.
Any idea of what could be done here, if anything?
Edit: An update
I've got things somewhat closer...but now I've run into another related issue. Here's my current drag-and-drop.handlebars:
<ul class="sortable-list">
{{#each dad}}
{{ drag-and-drop-item self=this dadi=../field }}
{{/each}}
</ul>
Here's drag-and-drop-item.handlebars:
<li class="sortable-item">{{ dragAndDrop self dadi }}</li>
And here's drag_and_drop_helpers.js:
Ember.Handlebars.helper("dragAndDrop", function(context,value) {
return this.self.get(value);
});
I realize that context isn't being used above, I've got it in there because of testing I was doing.
Here's the thing...with the code above, I get the correct 'this'. If I return "this.self.get('displayName');" in the helper, it works. However, the ../field isn't being returned in the template. I so wish you could have nested {{}}'s...then this would work!
To get the ../field working, I found I could make the following change to drag-and-drop.handlebars:
<ul class="sortable-list">
{{#each dad}}
{{#if ../field}}
{{ drag-and-drop-item self=this dadi=../field }}
{{/if}}
{{/each}}
</ul>
But if I do this, 'this' isn't the right one, so I'm getting 'displayName' through, but the wrong context.
It seems I can have one, or the other, but not both.
I could really use some help here.
I got it figured out, after quite a few days of searching and trying lots of things.
I did not realize that you could use "view." to pull the information.
Here's the call to this:
{{ drag-and-drop dad=unselectedPermissions field="displayName" }}
Here's the working drag-and-drop.handlebars:
<ul class="sortable-list">
{{#each dad}}
{{ drag-and-drop-item self=this dadi=view.field }}
{{/each}}
</ul>
Here's the working drag-and-drop-item.handlebars:
<li class="sortable-item">{{ dragAndDrop dadi }}</li>
Here's the working drag_and_drop_helpers.js:
Ember.Handlebars.helper("dragAndDrop", function(value) {
return this.self.get(value);
});
I'm hoping this helps someone in the future so they don't repeat mistakes I've made.
Now, on to figuring out the coding for the component.js...

How to use an HTML minifier with underscore templates

I have some templates for my frontend code, like:
<div class="row">
<div class="my-header col-md-1">
<!-- comments -->
{{ title }}
</div>
<div class="my-container col-md-11">
{% if (content) { %}
{{ content }}
{% } else { %}
<p>Empty</p>
{% } %}
</div>
</div>
And I'm using grunt-contrib-jst to store them all in a single file and then on another build step will be included in a single JS file and that file is pushed to the CDN. This part is working perfectly, but I want to use the processContent option to minify the HTML template code, which contains Undercore template delimiters (<%...%> replaced with {% ... %}, <%= ... %> replaced with {{ ... }}).
I wanted to use html-minifier but it doesn't actually minimize anything, apparently because it tries to parse the template as HTML-only (and fails because of the templating tags).
Is there any Node package / function that allows me to minimize this kind of templates? I would like to use comments and whitespace in source templates but strip everything unnecessary on the resulting build file.
Right now I have this on my JST settings:
processContent: function (content) {
return content
.replace(/^[\x20\t]+/mg, '')
.replace(/[\x20\t]+$/mg, '')
.replace(/^[\r\n]+/, '')
.replace(/[\r\n]*$/, '\n');
},
...
But I want to minimize everything possible, that's why I tried with html-minifier.
Thanks!
I can't really help with minimising the underscore template delimiters, still trying to find out the best way to do that myself, but you might want to consider running your templates through htmlclean. I use it with r.js and it works really well for stripping out newlines and spaces in the code. Example usage:
var htmlclean = require('htmlclean');
// ...
processContent: function (content) {
return htmlclean(content)
.replace(/^[\x20\t]+/mg, '')
.replace(/[\x20\t]+$/mg, '')
.replace(/^[\r\n]+/, '')
.replace(/[\r\n]*$/, '\n');
},
I've had no problems with using this on HTML that has underscore templates in there. I hope it helps you.

Categories

Resources