Define object & pass it to a partial - handlebars.js

What I want to do:
{{>myPartial foo={bar:1} }}
I want to define an object while passing it to a partial. Is that possible?
I know it's possible to pass an existing object like
{{>myPartial foo=foo}}
But I want to define my object within my markup.
Why? Well basically because it's just to define layout. I want to avoid to determine layout decisions on the backend.
My partial is a table layout, and I want to hide specific columns.
But instead of using multiple properties like
{{>myPartial hideFoo=true hideBar=true}}
I want to use a single object hide
{{>myPartial hide={foo:true,bar:true} }}

You can pass a new context to a partial:
{{> myPartial context }}
Example:
var data = {
title: "Foo Bar",
foo: ["foo1", "foo2"],
bar: ["bar1", "bar2"],
hide: {
foo: true,
bar: false
}
};
var content = "{{title}} {{> myPartial hide }}";
var partialContent = "<div class=\"{{#if foo}}hideFoo{{/if}} {{#if bar}}hideBar{{/if}}\">Hide</div>";
var template = Handlebars.compile(content);
Handlebars.registerPartial("foo", partialContent);
template(data);
Output:
<div class="hideFoo hideBar">Hide</div>
Another way is to pass a JSON string, instead of an object, using a helper in the way:
//helper
Handlebars.registerHelper("parseJSON", function(string, options) {
return options.fn(JSON.parse(string));
});
//template
{{#parseJSON '{"foo": true,"bar": true}'}}
{{> myPartial}}
{{/parseJSON}}
Demo:
//Compile main template
var template = Handlebars.compile($("#template").html());
//Register partial
Handlebars.registerPartial("myPartial", $("#myPartial").html());
//Register parseJSON helper
Handlebars.registerHelper("parseJSON", function(string, options) {
return options.fn(JSON.parse(string));
});
//Your data
var data = {
title: "Foo Bar"
};
document.body.innerHTML = template(data);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js"></script>
<!-- template.html -->
<script id="template" type="text/x-handlebars-template">
<h1>{{title}}</h1>
<h3>First Partial:</h3>
{{#parseJSON '{"foo": true,"bar": false}'}}
{{> myPartial}}
{{/parseJSON}}
<h3>Second Partial:</h3>
{{#parseJSON '{"foo": false,"bar": false}'}}
{{> myPartial}}
{{/parseJSON}}
</script>
<script id="myPartial" type="text/x-handlebars-template">
<div>hide.foo: {{foo}}</div>
<div>hide.bar: {{bar}}</div>
</script>

Related

In Polymer 1 how can I wrap a distributed template with another element?

I have a custom element (let's say my-view) which receives as effective children a template with some annotations for the data binding.
How can I wrap the distributed template with another custom element, let's say paper-item?
This is my working code.
<my-view>
<template>[[ item.name ]]</template>
</my-view>
Inside my-view I have
<template id="Repeater" is="dom-repeat">
</template>
and
_templatize() {
const repeater = this.$.Repeater
const template = this.queryEffectiveChildren('template')
repeater.templatize(template)
}
What I want to achieve is wrapping the template effective children with another custom element (let's say paper-item).
Something like
_templatize() {
const repeater = this.$.Repeater
const template = this.queryEffectiveChildren('template')
const item = this.create('paper-item')
item.appendChild(template.content)
repeater.templatize(item)
}
which of course doesn't work.
Perhaps I understood you wrong, but you don't create a page structure like the example you gave. Use the HTML elements first, and spice it up with javascript, if needed.
<dom-module id="my-view">
<template>
<template is="dom-repeat" items="[[anArrayWithStrings]]" as="someValue">
<paper-item>[[someValue]]</paper-item>
</template>
<template is="dom-repeat" items="[[anArrayWithObjects]]" as="employee">
<paper-item two-line>
<div>[[employee.name]]</div>
<div>[[employee.title]]</div>
</paper-item>
</template>
</template>
<script>
Polymer({
is: 'my-view',
properties: {
anArrayWithStrings: {
type: Array,
value: function() { return ['firstOne', 'secondOne', 'thirdOne']; }
},
anArrayWithObjects: {
type: Array,
value: function() { return [
{'name': 'Sarah', 'title': 'accountant'},
{'name': 'Ingrid', 'title': 'engineer'} ]; }
},
},
ready: function() {
//enter code here
},
</script>
</dom-module>
I just wrote this on freehand, without testing, so there could be some faulty code in there, but this is an example of how it could look like.

Meteor Block Helper that acts like a template

Here's what I want, a custom block helper that can act like a template, monitoring for it's own events etc. The html would look like this:
{{#expandable}}
{{#if expanded}}
Content!!!
<div id="toggle-area"></div>
{{else}}
<div id="toggle-area"></div>
{{/if}}
{{/expandable}}
And here's some javascript I have put together. This would work if I just declared the above as a template, but I want it to apply to whatever input is given to that expandable block helper.
Template.expandableView.created = function() {
this.data._isExpanded = false;
this.data._isExpandedDep = new Deps.Dependency();
}
Template.expandableView.events({
'click .toggle-area': function(e, t) {
t.data._isExpanded = !t.data._isExpanded;
t.data._isExpandedDep.changed();
}
});
Template.expandableView.expanded = function() {
this._isExpandedDep.depend();
return this._isExpanded;
};
I know I can declare block helpers with syntax like this:
Handlebars.registerHelper('expandable', function() {
var contents = options.fn(this);
// boring block helper that unconditionally returns the content
return contents;
});
But that wouldn't have the template behavior.
Thanks in advance! This might not be really possible with the current Meteor implementation.
Update
The implementation given by HubertOG is super cool, but the expanded helper isn't accessible from within the content below:
<template name="expandableView">
{{expanded}} <!-- this works correctly -->
{{content}}
</template>
<!-- in an appropriate 'home' template -->
{{#expandable}}
{{expanded}} <!-- this doesn't work correctly. Expanded is undefined. -->
<button class="toggle-thing">Toggle</button>
{{#if expanded}}
Content is showing!
{{else}}
Nope....
{{/if}}
{{/expandable}}
In the actual block helper, expanded is undefined, since the real thing is a level up in the context. I tried things like {{../expanded}} and {{this.expanded}}, but to no avail.
Strangely, the event handler is correctly wired up.... it fires when I click that button, but the expanded helper is simply never called from within the content, so even console.log() calls are never fired.
Meteor's new Blaze template engine solves this problem quite nicely.
Here's an excerpt from the Blaze docs showing how they can be used.
Definition:
<template name="ifEven">
{{#if isEven value}}
{{> UI.contentBlock}}
{{else}}
{{> UI.elseBlock}}
{{/if}}
</template>
Template.ifEven.isEven = function (value) {
return (value % 2) === 0;
}
Usage:
{{#ifEven value=2}}
2 is even
{{else}}
2 is odd
{{/ifEven}}
You can achieve this by making a helper that returns a template, and passing the helper options as a data to that template.
First, make your helper template:
<template name="helperTemplate">
<div class="this-is-a-helper-box">
<p>I'm a helper!</p>
{{helperContents}}
</div>
</template>
This will work as a typical template, i.e. it can respond to events:
Template.helperTemplate.events({
'click .click-me': function(e, t) {
alert('CLICK!');
},
});
Finally, make a helper that will return this template.
Handlebars.registerHelper('blockHelper', function(options) {
return new Handlebars.SafeString(Template.helperTemplate({
helperContents: new Handlebars.SafeString(options.fn(this)),
}));
});
The helper options are passed as a helperContents param inside the template data. We used that param in the template to display the contents. Notice also that you need to wrap the returned HTML code in Handlebars.SafeString, both in the case of the template helper and its data.
Then you can use it just as intended:
<template name="example">
{{#blockHelper}}
Blah blah blah
<div class="click-me">CLICK</div>
{{/blockHelper}}
</template>

assemble - Render a list of strings as Handlebars partial

Using the assemble i actually stuck on a problem which i can't fix myself.
I'm defining a bunch of widgets in the YAML front matter section and including an partial aside {{> aside}}. Until here everything works as expected!
What i'm trying to do now, is to take the list widgets and rendering my partials within the aside template. But anyhow it does not work as expected.
src/templates/layouts/layout-default.hbs
---
layout: src/templates/layouts/layout-default.hbs
widgets:
- widget_link-list
- widget_welcome-message
---
<section role="main">
<h1>Template TwoCol</h1>
{{> body }}
</section>
<aside role="complementary">
{{> aside}}
</aside>
src/templates/partials/aside.hbs
{{#each widgets}}
{{.}}
{{/each}}
Using {{.}} prints my above defined list as string. But if i try to do {{> .}} this, the console drops the following warning:
Warning: The partial . could not be found Use --force to continue.
I found a way by creating a custom helper that can be invoked from any Handlebars template. Now i'm able to use {{renderPartial 'partialName' context}}.
In my template:
var aside = ['my-widget-a','my-widget-b','my-widget-x'];
{{#each aside}}
{{renderPartial this ../this}}
{{/each}}
Javascript Module
module.exports.register = function (Handlebars, context) {
Handlebars.registerHelper("renderPartial", function (name) {
var fn,
template = Handlebars.partials[name];
if (typeof template !== 'Function') {
// not compiled, so we can compile it safely
fn = Handlebars.compile(template);
} else {
// already compiled, just reuse it
fn = template;
}
var output = fn(context).replace(/^\s+/, '');
return new Handlebars.SafeString(output);
});
};

passing values to meteor partials

I'm learning meteor to build quick website prototypes.
I'm trying to understand how to generate a set of values to populate the site templates and partials.
I have a layout.html template
<template name="layout">
<div class="container">
<header role="banner">
{{>site-header}}
</header>
<h1>This is {{siteLogo}}</h1>
<main role="main">
{{ yield }}
</main>
<footer role="contentinfo">
{{> site-footer }}
</footer>
</div>
</template>
in main.js I define the following:
Meteor.startup(function(){
Session.set('siteLogo', 'the logo');
});
Template.site-header.helpers({
siteLogo: function(){ return Session.get('siteLogo'); }
});
Template.layout.helpers({
siteLogo: function(){ return Session.get('siteLogo'); }
});
With this i can pass the value of siteLogo to layout.html.
I have a site-header.html partial
<template name="site-header">
<h1>{{siteLogo}}</h1>
</template>
I can't seem to be able to pass the value of siteLogo to the partial. Is there a way to do that?
Is it necessary to create a Session variable to pre-fill some values or can i just create a json settings list and access the value globally?
something that would go in main.js, like the yaml config file in a jekyll site:
siteSettings = [
{
siteLogo: "some brand name",
otherValue: "something else"
}
]
update
I'm a bit confused, I'm must be doing something wrong.
I've created a quick new meteor app to test this.
I have main.html
<head>
<title>handlebar-helper</title>
</head>
<body>
{{> header}}
{{> hello}}
{{> footer}}
</body>
<template name="hello">
<h1>Hello World!</h1>
{{greeting}}
<input type="button" value="Click" />
</template>
<template name="header">
<header>
<h1>{{ headline }}</h1>
<p>tagline</p>
</header>
</template>
<template name="footer">
<footer role="contentinfo">
<h1>{{ headline }}</h1>
<small>copyright</small>
</footer>
</template>
And main.js
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to handlebar-helper.";
};
Template.hello.events({
'click input' : function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
Meteor.startup(function(){
Session.set('headline', 'My fancy headline');
});
Handlebars.registerHelper('headline', function(){
return Session.get('headline');
});
}
if (Meteor.isServer) {
// server code here
}
And i can't still pass the value of headline into >header of >footer
if I try to put the Session.set into the Meteor.isServer block, I get a syntax error, Session is not defined
Cheers
Do you have a Template.site-header.helpers function declared for siteLogo? If not it won't work - you can't use a helper from another template. If you need to use siteLogo in a variety of places, it's best to use a Handlebars block helper, as these can be accessed by any template.
UPDATE
The Handlebars helper would just look like this:
Handlebars.registerHelper('siteLogo', function() {
return Session.get('siteLogo');
});
However, if you've already got a siteLogo helper in the site-header Template, it suggests something else is wrong, like a typo in a template or helper name. Is there an error in the console?
UPDATE 2
If you want to use a dictionary-style structure to store reactive data, you can do something like this:
Session.set('myDict', {foo: 1, bar: 2});
Handlebars.registerHelper('myDict', function(key) {
return Session.get('myDict') ? Session.get('myDict')[key] : null;
});
And then use this in your template: {{myDict 'foo'}}. Obviously, the format above would work fine in a tempate helper as well, but it would only be accessible from within that template. The ternary operator is just to check that myDict has been initialised before it lets a template try to look up one of the keys, which is a common Meteor problem on page load.
Incidentally, if you're finding Session variables a cumbersome way to deal with reactive dictionary-like data structures, it's pretty easy to roll your own. This is the best introduction.

Handlebars helper for template composition

I have a Handlebar helper to invoke a template within a template,
the usage is this :
applyTemplate subTemplateId arg1=123 arg2="abc" ...
It is also possible to pass html content
{{# applyTemplate "tli" a=1 b="y"}}
... any content here will get passed to the sub template with {{content}}
{{/ applyTemplate }}
This jsFiddle illustrates how it works : http://jsfiddle.net/maxl/ywUjj/
My problem : I want the variables in the calling scope to be accessible
in the sub templemplate, in the jsFiddle, notice how {{topLevelVar}}
is not available.
Thanks
From this example i would say you can use fn to access the context in your helper method
http://yehudakatz.com/2010/09/09/announcing-handlebars-js/
applyTemplate: function(context, fn) {
for(var i=0, j=context.length; i < j; i++) {
buffer.push(fn(context[i]))
}
}
Where fn is the inner "template" part, and context the model that gets applied to it.
Starting from the solution on http://jsfiddle.net/dain/NRjUb/ we can achieve the same result but with inline templates as:
<script id="topLevel" type="text/x-handlebars-template">
{{# defTpl "test1"}}
La plantilla <b>diu</b> {{part}},{{../topLevelVar}}
{{/ defTpl }}
{{# each sub }}
Iplant-->{{eTpl "test1" part=this}}--fi plant<br>
{{/each}}
</script>
And registering the handlebars helpers like:
(function()
{
var h={};
Handlebars.registerHelper('defTpl', function(name, context){
// the subtemplate definition is already compiled in context.fn, we store this
h[name]=context.fn;
return "";
});
// block level /inline helper
Handlebars.registerHelper('eTpl', function(name, context){
if (!h[name]) return "Error , template not found"+name;
var subTemplate = h[name];
//if this isn't a block template , the function to render inner content doesn't exists
var innerContent = context.fn?context.fn(this):"";
var subTemplateArgs = $.extend({}, context.hash, {content: new Handlebars.SafeString(innerContent)});
return new Handlebars.SafeString(subTemplate(subTemplateArgs))
});
})();
And calling this with:
var _template = Handlebars.compile($('#topLevel').html());
$('body').append(_template({topLevelVar:123, content:"cascading",sub:[45,30,12]}));
Hope this helps :)
Add "../" before topLevelVar to access the parent context.
For example:
{{../topLevelVar}}
<script id="tli" type="text/x-handlebars-template">
<tr><td>{{a}}----> {{content}} <----- {{b}}</td></tr>
</script>
<script id="zaza" type="text/x-handlebars-template">
<table>
{{# applyTemplate "tli" a=1 b="y"}}<input type="text" value='a'>{{../topLevelVar}}{{/ applyTemplate }}
{{# applyTemplate "tli" a=2 b="z"}}<input type="text" value='b'>{{/ applyTemplate }}
</table>
</script>

Resources