What's a clean way to have a conditional container in handlebars? - handlebars.js

When there is a link present, we want something like this HTML:
<img src="{{src}}"></img>
When there is no link present, we want something like this HTML:
<img src="{{src}}"></img>
Is there a clean way to do this? I consider the following solution bad, because it's dangerous to have to separately remember to open and close the <a> tag:
{{#if url}}<a href="{{url}}" title="{{title}}" target="_blank">{{/if}}
<img src="{{src}}">
{{#if url}}</a>{{/if}}
I considered using a block helper, but can't think of how to do so without adding more complexity. Maybe something like:
{{#linkWrap url}}<img src="{{src}}">{{/linkWrap}}
But then it's hard to see how we set the title and target and everything gets awkward.

I think you are on the right track, but I would recommend using a Handlebars Partial Block instead of a Block Helper. This will allow to pass one piece of template (the block) to another piece of template by which it will be wrapped (the partial).
Handlebars provides us with {{> #partial-block }} as a way to render a block of template within a partial. We can use this to create our "linkWrap" partial:
{{#if link}}
<a href="{{link.url}}" target="{{link.target}}" title="{{link.title}}">
{{> #partial-block}}
</a>
{{else}}
{{> #partial-block}}
{{/if}}
This gives us a clean and simple partial that will allow us to wrap any section of our template with a link as long as we have a link object to pass to our partial. Note that I have opted to use an object to represent the link so that I can pass a single parameter to the partial instead of passing the url, title, etc. properties individually.
For anywhere we wish to render a link around some markup in our template, we can do so in the following way:
{{#> linkWrap link=link}}
<img src="{{image.src}}">
{{/linkWrap}}
If the link object is null or undefined, the img element will be rendered without a parent anchor element.
I have created a supplementary fiddle for reference.

Related

Handlebars- block helper inside block helper

I'm new to Handlebars and I wanted to know if I can use block helper inside a block helper, something like this:
{{#each person}}
{{each family_member}}{{family_relation}}
{{/family_member}}
{{/person}}
I've tried to do it with a simple helpers in http://tryhandlebarsjs.com
and it didn't work, but I couldn't find any place saying if it can work or not....
any help?
Thanks
You can use block helpers inside block helpers. Provided they are used the correct way.
{{#each person}}
{{#each family_member}}
{{family_relation}}
{{/each}}
{{/each}}
You can actually see a plunkr here : http://plnkr.co/edit/H9XlaQG99etHb4Fwx9pp?p=preview

Template.body vs Template.myTemplate

I am currently going through the Meteor tutorial (https://www.meteor.com/try), and have come across something about Templates that puzzles me.
In the tutorial, a simple "Todo List" application gets created. In this app, the following HTML is placed into the simple-todos.html file:
<!-- simple-todos.html -->
<head>
<title>Todo List</title>
</head>
<body>
<div class="container">
<header>
<h1>Todo List</h1>
</header>
<ul>
{{#each tasks}}
{{> task}}
{{/each}}
</ul>
</div>
</body>
<template name="task">
<li>{{text}}</li>
</template>
Then, the following JavaScript is placed into the simple-todos.js file:
// simple-todos.js
Tasks = new Mongo.Collection("tasks");
if (Meteor.isClient) {
// This code only runs on the client
Template.body.helpers({
tasks: function () {
return Tasks.find({});
}
});
}
At this point, the example works exactly as intended. However, as I poke around in the documentation, as well as look at other examples on the web, I have noticed slightly different syntax: using Template.myTemplate instead of Template.body.
So, out of curiosity, I altered my JavaScript file to read:
Template.task.helpers({ ...
instead of:
Template.body.helpers({ ...
However, when I run the application now, the client does not display the data from the collection. I don't get any errors about undefined types, like I do if I misspell the template name in the JavaScript, so it seems that it is resolving the template correctly. But why isn't it getting used or rendered?
And to go a little further: when is it appropriate to use Template.myTemplate and when is it appropriate to use Template.body?
The helpers code only works for the template it's attached too.
So, code that works for Template.task will only apply to templates named "task".
Template.body is like the one-off that exists because it would be weird if it didn't. It's a way for you to specifically target the <body>, even though technically, there's no template named "body".
So, what is going on:
Parent template = body
Child template = task
Your logic says:
In the parent template, for each task that we find, render an instance of the child template "task".
If you change your helper from body to task, you won't get any output at all, unless you mimic the pattern that's already happening:
<template name="task">
{{#each tasks}}
do something
{{/each}}
</template>
That's because <body> is the parent template, and you should treat it as such:
<template="body>stuff that normally goes in an HTML body here</template>
When you remove the helpers for the body, then no data gets displayed at all because helpers pass data into the template. And with no helper for the template i.e. the body, you get no data.

Printing HTML code inside Meteor Template

How can I print HTML code inside a Meteor template using handle/spacebars?
When I try to manipulate the <div> element using a simple variable containing a style="" code, it generates an error. For example:
<div {{style}}>
// Something in here.
</div>
Will fail if {{style}} is something along the line of 'style="something: something;"' set from the Template.helpers.
How can I print HTML code inside the template?
What you're trying to do here in particular:
<div {{style}}>
<!-- Something in here. -->
</div>
With {{style}} evaluating to 'style="key: value;"' is impossible in Blaze, however it will work if {{style}} evaluates to an object {style: "key: value;"}. Alternatively, this will also work:
<div style="{{style}}">
<!-- Something in here. -->
</div>
With {{style}} evaluating to the string key: value.
The triple brace {{{helper}}} cannot be used to insert attributes, but it can otherwise be used to insert arbitrary HTML nodes without escaping. If you use it, be sure you aren't opening up an XSS hole.
See this meteorpad.
I don't know if it's possible to use variables inside an HTML tag, but if want to pass HTML code from your variable to the client, simply use {{{variable}}} instead of {{variable}}.

Isolating template regions in meteor for easier DOM navigation

It is very useful to use template.find to locate DOM elements inside a specific template instance. But what happens when the template iterates {{#each}} over some tags without using a sub-template?
<template name="top">
{{#each items}}
<img src="{{src}}">
Click me
{{/each}}
</template>
Tempalte.events(
'click a': (event, template) ->
template.find('img') # This doesn't do the trick
# Is there a better way?
)
Is there a way to easily access the img element associated with the click event?
I know I can use event.target or create another template an use it inside the {{#each}} block. I wonder if there is a better/shorter way to do it.
You could add an #index to the img to make it possible to select it (using js or CSS). How to get index in Handlebars each helper?

Display Handlebars.js context in a template

Is there a variable passed into every handlebar.js template that contains all the context content that is accessible by the template?
e.g. I'm creating a template, but I don't know all the context content accessible by the template. I want to be able to type into the template {{ debug }} and handlebars.js will spit out all the context content into the HTML
You can use the following code to iterate through this object:
{{#each this}}
{{#key}}: {{this}}
{{/each}}
or a similar piece of code iterating through #root object:
{{#each #root}}
{{#key}}: {{this}}
{{/each}}
Handlebars has built-in helper log.
You just need to set logging level to DEBUG.
Handlebars.logger.level = 0;
Then use helper:
{{log this}}
EDIT: Sorry, this will not write context to HTML, helper uses console.log. For outputting to HTML you need to write custom helper that will use for example JSON.stringify.
Though this question is somewhat old, someone might find this usefull. You can just dump the handlebars current context into plain text with;
{{{.}}}

Resources