Home » Javascript » Passing variables through handlebars partial

Passing variables through handlebars partial

Posted by: admin November 29, 2017 Leave a comment

Questions:

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?

Answers:

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

Questions:
Answers:

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

Questions:
Answers:

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. 🙂

Questions:
Answers:

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

Questions:
Answers:

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.

Questions:
Answers:

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>

Questions:
Answers:

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).