Handlebars decorator in loop - javascript

For few days, I've begun to use Handlebars and I'm currently trying Decorators. I've understood how it works when it remains simple :
Ex: decorate the name alone : bill gaTes -> Mr Bill GATES
I then tried to use decorator in a "each" loop, to do exactly the same as before but with a list of people instead of just one. The problem is: when I am in the decorator function, I need to know which element (from the array) I am looking at.
So I would like to either give in argument the index of the "each" loop (then, as I have access to the data, I could retrieve the current element) or the current element.
I tried to use #index (which usually works well), but I get undefined when debugging.
And I can't find a way to get the current element in my decorator.
Here is the code:
index.html
<!doctype html>
<html>
<head>
<title>Test Handlebars Decorators 4</title>
</head>
<body>
<div id = "main"> </div>
<script id = "rawTemplate" type = "text/x-handlebars-template">
{{#each this}}
Decorated: {{formatGenderHelper this}}
{{* activateFormatter #index}}
<br />
{{/each}}
</script>
<script type = "text/javascript" src = "js/lib/handlebars.min-latest.js"></script>
<script type = "text/javascript" src = "js/lib/jquery-2.2.4.min.js"></script>
<script type = "text/javascript" src = "js/data.js"></script>
<script type = "text/javascript" src = "js/index.js"></script>
</body>
</html>
data.js
var data = [{
"firstname": "Bill",
"lastname": "Gates",
"gender": "MALE"
}, {
"firstname": "Hillary",
"lastname": "Clinton",
"gender": "FEMALE"
}];
function formatMale(author) {
return "M. " + author.firstname + " " + author.lastname.toUpperCase();
}
function formatFemale(author) {
return "Mme " + author.firstname + " " + author.lastname.toUpperCase();
}
Handlebars.registerDecorator('activateFormatter', function(program, props, container, context) {
var genderHelper;
var gender = context.args[0] || context.data.root[0].gender;
switch(gender) {
case "MALE":
genderHelper = formatMale;
break;
case "FEMALE":
genderHelper = formatFemale;
break;
default:
console.log('Gender format not set. Please set before rendering template.');
genderHelper = function() {};
}
container.helpers = {
formatGenderHelper: genderHelper
};
});
index.js
// Get the template
var rawTemplate = $('#rawTemplate').html();
// Compile the template
var compiledTemplate = Handlebars.compile(rawTemplate);
// Apply the template on the data
var content = compiledTemplate(data);
// Finally, re-inject the rendered HTML into the DOM
$('#main').html(content);
If you need further information, please let me know.
Thanks you for helping :)

There are two issues that make your example fail. One is a slight issue with your code, and the other is the way decorators seem to work, which is essentially just "not in loops" (unless you're using partials, see the last section of this answer).
First, you haven't told the decorator what to do with the #index that you're passing to it. You could change the decorator function, but since you have the decorator inside the #each block, the this is the same one that gets passed to formatGenderHelper, meaning that we can pass the decorator this.gender which resolves to just the gender string. This means that the writer of the template knows exactly what they're passing to the decorator, and the logic isn't trying to second-guess what the template is telling it.
index.html
...
{{#each this}}
Decorated: {{formatGenderHelper this}}
{{* activateFormatter this.gender}}
<br />
{{/each}}
...
Also, I figure you are basing your example on Ryan Lewis's sitepoint demo. One problem/limitation his code has that isn't immediately obvious (because in the simple case it isn't even an issue), is that his decorator overwrites of all of the available helpers except for the formatting one it provides. To avoid "Error(s): TypeError: Cannot read property 'call' of undefined" errors when doing things that are a little bit more complex, I recommend using this in your decorator function.
data.js
Handlebars.registerDecorator('activateFormatter', function(program, props, container, context) {
// leaving out the code here for brevity
container.helpers = Object.assign({}, container.helpers, {
formatGenderHelper: genderHelper
});
});
The second issue is a general one with using decorators in loops, that is simply a result of the way Handlebars works with decorators. According to the decorators API doc,
Decorators are executed when the block program is instantiated and are passed (program, props, container, context, data, blockParams, depths).
This means (afaict) that when the template for the block is first instantiated, meaning before any of the other helpers or programs that will need to run on the block, it executes the decorator, and the decorator makes the modifications it needs, before executing the program that created the block (in this case the #each). This means that to get the decorator to differentiate each iteration of the #each differently, it needs to run in a grandchild block of the #each, so it is executed after the #each but before the formatGenderHelper, however, if you're doing this and redefining the decorated helper with each iteration based on the iterated-over context, you're better off just registering a helper that has the gender formatting logic baked in.
However, that's sort of a non-answer. So, to answer your question, I've found that you can make handlebars do what you're trying to do, but it's sort of a hack. Since the trick is that you need the block with the decorator to be rendered from a sub-sub block of the #each we can render it in a partial. To do this we can stick it in another file and register that as a partial, but that's not really convenient, so we have two better options. 1) we can wrap the code in an undefined partial-block and let Handlebars render it as the fallback block when that partial fails, or (the slicker option) 2) we can use the provided builtin decorator #*inline to define an inline partial.
Fail-over partial block method
index.html
...
{{#each this}}
{{#>nothing}}
Decorated: {{formatGenderHelper this}}
{{* activateFormatter this.gender}}
<br />
{{/nothing}}
{{/each}}
...
#*inline method
index.html
...
{{#*inline "formattedAuthor"}}
Decorated: {{formatGenderHelper this}}
<br />
{{/inline}}
{{#each this}}
{{#>formattedAuthor}}
{{* activateFormatter this.gender}}
{{/formattedAuthor}}
{{/each}}
...
The key here is that we use our *activeFormatter decorator to dynamically reconfigure the partial each time it gets called. The second, inline example demonstrates this more clearly. There may certainly be other good use-cases, but this is where I see decorators really shining, i.e. allowing us to dynamically reconfigure the logic or helpers of partials right from where we call them.
However, there is a caveat: if our partial uses a helper that is only provided in a decorator that is called from outside of that partial's definition (like we have above in example 2) the partial will not be able to find the helper if that decorator is not called in the right place. This is also why it's better to use either of the two methods above for providing the partial: we keep the definition of the partial in the same file as the decorator call, so we can know that the helper is only provided dynamically and not registered globally.

Related

Reusing a blaze template, how do I access other template's helper functions?

I'm trying to set up an MDC dialog warning. Instead of copy-pasting it into every view that requires it, I'm wrapping the dialog in its own template. The template seems to work, the dialog opens up and functions as normal, however, I can't set a helper function for it that works. I tried using the helper function of the parent template, and even creating the new template its own js file. Neither of these solutions grab the data correctly.
<template name="transactionAlert">
...
<div class="mdc-dialog__content" ><p>Are you sure you wish to continue with this transaction? It could cost up to: <b class="warning-value">${{maxCost}} USD</b></p>
...
</template>
<template name="transactionCreate">
...
{{>transactionAlert}}
</template>
Template.transactionAlert.onCreated(function transactionAlertOnCreated() {
console.log('test')
})
Template.transactionAlert.helpers({
maxCost(){
console.log('test 2')
const instance = Template.instance()
return instance.maxTxCost.get().toString().slice(0,5);
}
})
I tried using the helper function of the parent template
Such problems are often caused by design issues, rather than missing or wrong implementation. If we consider the your transactionAlert to be stateless (it does not contain any relevant view logic or internal state management) then it should also neither access properties nor helpers that are out of it's scope.
Otherwise you will create such a tight coupling that it will throw back in your face in two years or so (when the refactoring session is calling).
In contrast the responsibilities of the parent Template are to
manage state of the data (subscriptions, data post-processing etc.)
check the conditions, whether the transactionAlert should appear or disappear
pass the proper parameters to the transactionAlert Template
As a consequence you may design your transaction alert as a parameterized template:
<template name="transactionAlert">
...
<div class="mdc-dialog__content" ><p>Are you sure you wish to continue with this transaction? It could cost up to: <b class="warning-value">${{maxCost}} USD</b></p>
...
</template>
As you can see it looks exactly the same. The difference is, that you remove the Template.transactionAlert.helpers and cause the Template to look for maxCost being passed to the template.
Now in your parent Template you will pass the data to the transactionalert, once the condition for alerting applies:
<template name="transactionCreate">
{{#if showAlert}}
{{>transactionAlert maxCost=getMaxCost}}
{{/if}}
</template>
where the helper is now:
Template.transactionCreate.helpers({
showAlert () {
return Template.instance().showAlert.get()
},
getMaxCost(){
const instance = Template.instance()
return instance.maxTxCost.get().toString().slice(0,5);
}
})
Because you need reactivity to show/hide the alert you will make use of the Template's internal Tracker:
Template.transactionCreate.onCreated(function () {
const instance = this
instance.showAlert = new ReactiveVar(false)
instance.autorun(() => {
const maxCost = instance.maxTxCost.get()
if (/* max cost exceeds limit */) {
instance.showAlert.set(true)
} else {
instance.showAlert.set(false)
}
})
})
Edit: Additional information on reactivity
Reactivity is a main concept of Meteor's client ecosystem. It bases on the Tracker package, which is linked to any Template instance. The guide to the reactive data stores explains the concept a bit further: https://guide.meteor.com/data-loading.html#stores

Helper loses parent context when wrapped in a custom block

I have a block of Handlebars that was rendering fine until I wrapped it in a custom block. Specifically there is a call to the parent context to get the Currency type. Bear in mind that this Handlebars block is wrapped in an each:
{{#each this.SubscriptionOptions.MonthlySubscriptions}}
So clearly I know where the problem is, I'm just not sure how to solve it.
Here is the block of Handlebars:
<p class="lead" style="font-size:40px">
{{#ifGreaterThanZero PricePerBillingPeriod}}
<strong>{{currency ../Currency}}{{priceFormat PricePerBillingPeriod}}</strong>
{{else}}
<strong>FREE</strong>
{{/ifGreaterThanZero}}
</p>
The piece that is now failing is this:
{{currency ../Currency}}
Here is the ifGreaterThanZero helper code:
Handlebars.registerHelper('ifGreaterThanZero', function(value, options) {
var intVal = parseInt(value);
if (intVal) {
return options.fn(this);
} else {
options.inverse(this);
}
});
I took a look at this, and it is in fact the subscription option itself, so it contains the PricePerBillingPeriod for example.
The question is, how do I get it so that reaching the parent context works again?
Okay, so I figured it out. You have to go up one more level. It's not necessarily parent context in the object sense, it's helper parent context and so the deeper you go, it's moving up the stack like a directory structure.
The new code looks like this:
<strong>{{currency ../../Currency}}{{priceFormat PricePerBillingPeriod}}</strong>

EmberJS - Text Binding Not Working

Ok, so let me preface this by saying I'm completely new to Ember. I'm having an interesting time just trying to get a basic binding to work. Here's my relevant code:
App.js
var App = Ember.Application.create({
rootElement: '#emberApp'
});
And routes.js:
App.Router.map(function () {
});
App.IndexRoute = Ember.Route.extend({
model: function () {
return { Foo: 3 };
}
});
And then here is my HTML document:
<div id="emberApp"></div>
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/handlebars.js"></script>
<script src="~/Scripts/ember-1.4.0.js"></script>
<script src="~/Scripts/EmberApp.js"></script>
<script src="~/Scripts/Routes.js"></script>
<script type="text/x-handlebars" data-template-name="index">
<div>The current foo value: {{Foo}}</div>
{{input valueBinding=Foo}}
</script>
The intended result is that when I type in the input field created, the value that is bound in the template changes at the same time. It should be fairly simple right? I have to be missing something.
The template correctly renders The current foo value: 3, and it renders a text field. However, typing anything into this field does nothing to the above binding. I have tried marking it with a valueBinding tag, as well as switching to Ember.TextField instead of a input helper. I've also tried making a custom Ember.Object.extend class and returning that as the model for the index route.
What am I missing in order to bind the text box value to the {{Foo}} value in the template?
EDIT - Ok, I've figured out it's because of the capitalization of the variable: foo works, but not Foo. Why is this?
I'm expecting to receive JSON results similar to this:
{
RemoteUserId: 0,
UserPhotoUrl: '....',
RemoteUserName: 'Bob',
}
I'm assuming this means I need to 'hide' these values by making controller wrappers for each element? ie:
remoteUserId: function() {
return this.get('RemoteUserId');
}.property()
I'm afraid you've been bitten by one of Embers naming conventions which is normally awesome as it usually means things just work, but occasionally will bite you if you're not aware of it.
Ember expects that Classes or Namespaces are capitalized and that instances are lowercase. When Ember sees the Foo property used in a binding it assumes it's a namespace and will then look for a global variable called Foo instead of a controller property.
When you use {{Foo}} in a template the behavior is slightly different as Ember will first check the current context (the controller) to see if the property exists there. If it does it uses that value, otherwise it will assume it's a namespace and look for it globally. Bindings don't work like templates due to performance concerns as you don't want to have to check two locations for a value in a binding that could be updated very frequently (like a textbox being typed in).
This is why you can use Foo in the template and it works:
<script type="text/x-handlebars" data-template-name="index">
<!-- This works! -->
<div>The current foo value: {{Foo}}</div>
</script>
But when you try to use Foo as part of a binding it won't work:
<script type="text/x-handlebars" data-template-name="index">
<!-- This doesn't work as Ember thinks Foo is global (i.e., a namespace) -->
{{input valueBinding=Foo}}
</script>
Your best bet is to just follow ember conventions and make sure all your property names start with a lowercase character. However, if you want to continue using properties in your controllers that start with a capital character then you will need to explicitly tell Ember that the property is from the controller and is not global when you try to use it in a binding:
<script type="text/x-handlebars" data-template-name="index">
<!-- Tell Ember Foo is in the controller which is what we want-->
{{input valueBinding=controller.Foo}}
</script>
Here is a Fiddle demonstrating everything written above:
http://jsfiddle.net/NQKvy/881/
Its because in Ember properties should start with a lowercase letter! Uppercase letters are globals.
so u could simple convert your JSON bevore import it into ember:
var decapitalize = function(source) {
var result = {};
for(var prop in source) {
result[prop.substr(0,1).toLowerCase() + prop.substr(1)] = source[prop];
}
return result;
};

How to see all available variables in handlebars template

I'm working on my first Ember.js app and am having some trouble connecting all the dots. It would be really helpful if I could just see all the variables available within a given handlebars template.
There is a related question, but you have to know the variable that is in scope to use it:
How do I add console.log() JavaScript logic inside of a Handlebars template?
How can I output all the variables?
a good option is to debug the value of 'this' in a template using the Handlebars helpers:
1.
{{#each}}
{{log this}}
{{/each}}
or,
2. similar to #watson suggested
{{#each}}
{{debugger}}
{{/each}}
and then drill in to the Local Scope Variables for 'this' in the Dev Tools
or alternatively, 3. you could log things directly from inside your Controller init method, such as:
App.UsersController = Ember.ArrayController.extend({
init: function() {
console.log(this);
console.log(this.getProperties('.'));
}
});
Make sure you try out Firebug - you'll get a different perspective on things, which I found helpful. But don't abandon chrome completely; you will need the Ember Inspector at some point.
I'm using the same debugging helper everyone recommends, and this is how Chrome displays it:
When I expand the same object in firebug, I get the following info, including the variables I was looking for (sources[]) and some other useful properties I hadn't seen in Chrome.
I created Barhandles a few years ago. It will use the Handlebars parser to produce the AST, and then extract variable references from it. The extractSchema method will — well — extract a schema. That schema is not based on JSON Schema or Joi or anything. It's a homegrown format that captures most of the things you could possibly extract from Handlebars template.
So, this barhandlers.extractSchema('{{foo.bar}}') produces:
{
"foo": {
"_type": "object",
"_optional": false,
"bar": {
"_type": "any",
"_optional": false
}
}
}
It will take into account that an {{#if expr}} will automatically make nested references optional. It correctly handles scope changes based on {{#with expr}} constructs, and it allows you to add support for your own custom directives as well.
http://nxt.flotsam.nl/barhandles
https://medium.com/east-pole/advanced-barhandles-4a7e64c1bc0d
We used it to do validation on the data structures that we passed into the template, and it was working pretty well for that purpose.
If you really need to dump the variables in your template, you can explore the template AST and output the content of the relevant nodes (see the compiler sources). This is not an easy task because you have to find your way through trials and errors, and the code is quite low-level and there are not so many comments.
It seems Handlerbars doesn't have a shortcut for what you're asking, so the steps would be:
precompile a template (see the command line source, I think the function is called handlebars.precompile())
explore the AST
You can do this by leveraging Handlebars.parseWithoutProcessing which takes the input template string. If you use TypeScript, that returns a specific type hbs.AST.Program. You can filter for only the moustache statements, and then iterate through these statements to get the variable names.
This method also supports Handlebars helpers, so you can get the key for that, but because of this, this function is a bit more complex as you'd need to check different properties on the moustache statement:
/**
* Getting the variables from the Handlebars template.
* Supports helpers too.
* #param input
*/
const getHandlebarsVariables = (input = '') => {
const ast = Handlebars.parseWithoutProcessing(input);
return ast.body
.filter(({ type }) => type === 'MustacheStatement')
.map((statement) => statement.params[0]?.original || statement.path?.original);
};
Here's the TypeScript version, which is a bit involved due to the conditional properties, but can help explain the types a bit more:
/**
* Getting the variables from the Handlebars template.
* Supports helpers too.
* #param input
*/
const getHandlebarsVariables = (input: string): string[] => {
const ast: hbs.AST.Program = Handlebars.parseWithoutProcessing(input);
return ast.body.filter(({ type }: hbs.AST.Statement) => (
type === 'MustacheStatement'
))
.map((statement: hbs.AST.Statement) => {
const moustacheStatement: hbs.AST.MustacheStatement = statement as hbs.AST.MustacheStatement;
const paramsExpressionList = moustacheStatement.params as hbs.AST.PathExpression[];
const pathExpression = moustacheStatement.path as hbs.AST.PathExpression;
return paramsExpressionList[0]?.original || pathExpression.original;
});
};
I've made a Codepen that illustrates this. Essentially, given the following template:
Hello, {{first_name}}! The lottery prize is {{formatCurrency prize_amount}}! Good luck!
It will use window.prompt to ask the user for their name and the prize amount. The example also implements a helper formatCurrency. You can see it here: https://codepen.io/tinacious/pen/GRqYWJE
The sample Ember app you mention defines its EmberObjects right in its app.js. So basically, what's available on the objects are the properties that are defined onto them there. (e.g. subreddit has a title, etc).
If you want a globally available way to dump an object's property schema out to the console, one approach would be to create a "debug" helper that walks the members of the passed-in contexts and writes them out. Something like:
Handlebars.registerHelper('debug', function (emberObject) {
if (emberObject && emberObject.contexts) {
var out = '';
for (var context in emberObject.contexts) {
for (var prop in context) {
out += prop + ": " + context[prop] + "\n"
}
}
if (console && console.log) {
console.log("Debug\n----------------\n" + out);
}
}
});
Then call it on whatever you want to inspect:
<div>Some markup</div>{{debug}}<div>Blah</div>
This will use whatever EmberObject is in scope, so pop it inside of an {{#each}} if you want to inspect the list elements, as opposed to the object with that list.
The variables available within a template are only constrained by the model you are using to render the template.
You should set a breakpoint in your app where you render the template and see what is in your model at that point, which will should you what you have available to put in your template.

Passing variables through handlebars partial

I'm currently dealing with handlebars.js in an express.js application. To keep things modular, I split all my templates in partials.
My problem: I couldn't find a way to pass variables through an partial invocation. Let's say I have a partial which looks like this:
<div id=myPartial>
<h1>Headline<h1>
<p>Lorem ipsum</p>
</div>
Let's assume I registered this partial with the name 'myPartial'. In another template I can then say something like:
<section>
{{> myPartial}}
</section>
This works fine, the partial will be rendered as expected and I'm a happy developer. But what I now need, is a way to pass different variables throught this invocation, to check within a partial for example, if a headline is given or not. Something like:
<div id=myPartial>
{{#if headline}}
<h1>{{headline}}</h1>
{{/if}}
<p>Lorem Ipsum</p>
</div>
And the invokation should look something like this:
<section>
{{> myPartial|'headline':'Headline'}}
</section>
or so.
I know, that I'm able to define all the data I need, before I render a template. But I need a way to do it like just explained. Is there a possible way?
Handlebars partials take a second parameter which becomes the context for the partial:
{{> person this}}
In versions v2.0.0 alpha and later, you can also pass a hash of named parameters:
{{> person headline='Headline'}}
You can see the tests for these scenarios: https://github.com/wycats/handlebars.js/blob/ce74c36118ffed1779889d97e6a2a1028ae61510/spec/qunit_spec.js#L456-L462
https://github.com/wycats/handlebars.js/blob/e290ec24f131f89ddf2c6aeb707a4884d41c3c6d/spec/partials.js#L26-L32
Just in case, here is what I did to get partial arguments, kind of. I’ve created a little helper that takes a partial name and a hash of parameters that will be passed to the partial:
Handlebars.registerHelper('render', function(partialId, options) {
var selector = 'script[type="text/x-handlebars-template"]#' + partialId,
source = $(selector).html(),
html = Handlebars.compile(source)(options.hash);
return new Handlebars.SafeString(html);
});
The key thing here is that Handlebars helpers accept a Ruby-like hash of arguments. In the helper code they come as part of the function’s last argument—options— in its hash member. This way you can receive the first argument—the partial name—and get the data after that.
Then, you probably want to return a Handlebars.SafeString from the helper or use “triple‑stash”—{{{— to prevent it from double escaping.
Here is a more or less complete usage scenario:
<script id="text-field" type="text/x-handlebars-template">
<label for="{{id}}">{{label}}</label>
<input type="text" id="{{id}}"/>
</script>
<script id="checkbox-field" type="text/x-handlebars-template">
<label for="{{id}}">{{label}}</label>
<input type="checkbox" id="{{id}}"/>
</script>
<script id="form-template" type="text/x-handlebars-template">
<form>
<h1>{{title}}</h1>
{{ render 'text-field' label="First name" id="author-first-name" }}
{{ render 'text-field' label="Last name" id="author-last-name" }}
{{ render 'text-field' label="Email" id="author-email" }}
{{ render 'checkbox-field' label="Private?" id="private-question" }}
</form>
</script>
Hope this helps …someone. :)
This can also be done in later versions of handlebars using the key=value notation:
{{> mypartial foo='bar' }}
Allowing you to pass specific values to your partial context.
Reference: Context different for partial #182
This is very possible if you write your own helper. We are using a custom $ helper to accomplish this type of interaction (and more):
/*///////////////////////
Adds support for passing arguments to partials. Arguments are merged with
the context for rendering only (non destructive). Use `:token` syntax to
replace parts of the template path. Tokens are replace in order.
USAGE: {{$ 'path.to.partial' context=newContext foo='bar' }}
USAGE: {{$ 'path.:1.:2' replaceOne replaceTwo foo='bar' }}
///////////////////////////////*/
Handlebars.registerHelper('$', function(partial) {
var values, opts, done, value, context;
if (!partial) {
console.error('No partial name given.');
}
values = Array.prototype.slice.call(arguments, 1);
opts = values.pop();
while (!done) {
value = values.pop();
if (value) {
partial = partial.replace(/:[^\.]+/, value);
}
else {
done = true;
}
}
partial = Handlebars.partials[partial];
if (!partial) {
return '';
}
context = _.extend({}, opts.context||this, _.omit(opts, 'context', 'fn', 'inverse'));
return new Handlebars.SafeString( partial(context) );
});
Sounds like you want to do something like this:
{{> person {another: 'attribute'} }}
Yehuda already gave you a way of doing that:
{{> person this}}
But to clarify:
To give your partial its own data, just give it its own model inside the existing model, like so:
{{> person this.childContext}}
In other words, if this is the model you're giving to your template:
var model = {
some : 'attribute'
}
Then add a new object to be given to the partial:
var model = {
some : 'attribute',
childContext : {
'another' : 'attribute' // this goes to the child partial
}
}
childContext becomes the context of the partial like Yehuda said -- in that, it only sees the field another, but it doesn't see (or care about the field some). If you had id in the top level model, and repeat id again in the childContext, that'll work just fine as the partial only sees what's inside childContext.
The accepted answer works great if you just want to use a different context in your partial. However, it doesn't let you reference any of the parent context. To pass in multiple arguments, you need to write your own helper. Here's a working helper for Handlebars 2.0.0 (the other answer works for versions <2.0.0):
Handlebars.registerHelper('renderPartial', function(partialName, options) {
if (!partialName) {
console.error('No partial name given.');
return '';
}
var partial = Handlebars.partials[partialName];
if (!partial) {
console.error('Couldnt find the compiled partial: ' + partialName);
return '';
}
return new Handlebars.SafeString( partial(options.hash) );
});
Then in your template, you can do something like:
{{renderPartial 'myPartialName' foo=this bar=../bar}}
And in your partial, you'll be able to access those values as context like:
<div id={{bar.id}}>{{foo}}</div>
Yes, I was late, but I can add for Assemble users: you can use buil-in "parseJSON" helper http://assemble.io/helpers/helpers-data.html. (Discovered in https://github.com/assemble/assemble/issues/416).
Not sure if this is helpful but here's an example of Handlebars template with dynamic parameters passed to an inline RadioButtons partial and the client(browser) rendering the radio buttons in the container.
For my use it's rendered with Handlebars on the server and lets the client finish it up.
With it a forms tool can provide inline data within Handlebars without helpers.
Note : This example requires jQuery
{{#*inline "RadioButtons"}}
{{name}} Buttons<hr>
<div id="key-{{{name}}}"></div>
<script>
{{{buttons}}}.map((o)=>{
$("#key-{{name}}").append($(''
+'<button class="checkbox">'
+'<input name="{{{name}}}" type="radio" value="'+o.value+'" />'+o.text
+'</button>'
));
});
// A little test script
$("#key-{{{name}}} .checkbox").on("click",function(){
alert($("input",this).val());
});
</script>
{{/inline}}
{{>RadioButtons name="Radio" buttons='[
{value:1,text:"One"},
{value:2,text:"Two"},
{value:3,text:"Three"}]'
}}

Categories