Ben
Ben

Reputation: 21249

handlebars - is it possible to access parent context in a partial?

I've got a handlebar template that loads a partial for a sub-element.

I would need to access a variable from the parent context in the calling template, from within the partial. .. doesn't seem to resolve to anything inside the partial.

Simplified code goes like this:

the template

    {{#each items}} 
        {{> item-template}}
    {{/each}}

the partial

    value is {{value}}

(obviously the real code is more complicated but it's the same principle, within the partial .. appears to be undefined.)


To show it's undefined, I've used a very simple helper whatis like this:

Handlebars.registerHelper('whatis', function(param) {
    console.log(param);
});

and updated the above code to this:

updated template

    {{#each items}} 
        {{whatis ..}}  <-- Console shows the correct parent context
        {{> item-template}}
    {{/each}}

updated partial

    {{whatis ..}}  <-- Console shows "undefined"
    value is {{value}}

Is there a way to go around that issue? Am I missing something?

EDIT: There's an open issue relating to this question on handlebars' github project

Upvotes: 61

Views: 36899

Answers (8)

Etherman
Etherman

Reputation: 1867

To get specifically the parent of the partial (where you may be several partials deep) then follow the other answers like SeanWM.

If you know that the parent is the main template then you can use @root which resolves to the top-most context no matter how deep you are.

e.g. {{@root.rootObject.rootProperty}}

It is a pity that ../../.. does not go up past a partial.

Upvotes: 0

SeanWM
SeanWM

Reputation: 16989

As of 2.0.0 partials now supports passing in values.

{{#each items}}
    {{> item-template some_parent_var=../some_parent_var}}
{{/each}}

Took me awhile to find this, hope it's useful for someone else too!

Upvotes: 18

swille
swille

Reputation: 841

I needed dynamic form attributes for something like this...

    {{#each model.questions}}
      <h3>{{text}}</h3>

          {{#each answers}}
                {{formbuilder ../type id ../id text}}
            {{/each}}

    {{/each}}

and a helper like so...

    Handlebars.registerHelper('formbuilder', function(type, id, qnum, text, options)
    {
        var q_type = options.contexts[0][type],
            a_id = options.contexts[1].id,
            q_number = options.contexts[0][qnum],
            a_text = options.contexts[1].text;


            return new Handlebars.SafeString(
                    '<input type=' + q_type + ' id=' + a_id + ' name=' + q_number + '>' + a_text + '</input><br/>'
            );
    });

Which produces...

<input type="checkbox" id="1" name="surveyQ0">First question</input>

My model is a big blob of arrays and objects mixed together. What's noteworthy is that using '../' like so '../type', passes in the parent model as the context, and without it, such as with 'id', it passes in the current model as the context.

Upvotes: 1

StefanHayden
StefanHayden

Reputation: 3669

The easiest way to pass the parent context to the partial is to do the loop inside the partial. This way the parent context is passed by default and when you do the loop inside the partial the {{../variable}} convention can access the parent context.

example fiddle here.

The Data

{
  color: "#000"
  items: [
    { title: "title one" },
    { title: "title two" },
  ]
}

The Template

<div class="mainTemplate">
  Parent Color: {{color}}
  {{> partial}}
</div>

The Partial

<div>
  {{#each items}}
    <div style="color:{{../color}}">
      {{title}}
    </div>
  {{/each}}
</div>

Upvotes: 4

pward123
pward123

Reputation: 734

Working fiddle (inspired by handlebars pull request #385 by AndrewHenderson) http://jsfiddle.net/QV9em/4/

Handlebars.registerHelper('include', function(options) {
    var context = {},
        mergeContext = function(obj) {
            for(var k in obj)context[k]=obj[k];
        };
    mergeContext(this);
    mergeContext(options.hash);
    return options.fn(context);
});

Here's how you'd setup the parent template:

{{#each items}} 
    {{#include parent=..}}
        {{> item-template}}
    {{/include}}
{{/each}}

And the partial:

value is {{parent}}

Upvotes: 22

James Andres
James Andres

Reputation: 1532

Just in case anyone stumbles across this question. This functionality exists now in Handlebars.

Do this:

{{#each items}} 
    {{! Will pass the current item in items to your partial }}
    {{> item-template this}} 
{{/each}}

Upvotes: 41

AndrewHenderson
AndrewHenderson

Reputation: 4972

I created an each Helper function that includes the parent key/values within the subcontext under the key parentContext.

http://jsfiddle.net/AndrewHenderson/kQZpu/1/

Note: Underscore is a dependency.

Handlebars.registerHelper('eachIncludeParent', function ( context, options ) {
var fn = options.fn,
    inverse = options.inverse,
    ret = "",
    _context = [];
    $.each(context, function (index, object) {
        var _object = $.extend({}, object);
        _context.push(_object);
    });
if ( _context && _context.length > 0 ) {
    for ( var i = 0, j = _context.length; i < j; i++ ) {
        _context[i]["parentContext"] = options.hash.parent;
        ret = ret + fn(_context[i]);
    }
} else {
    ret = inverse(this);
}
return ret;

});

To be used as follows:

{{#eachIncludeParent context parent=this}}
    {{> yourPartial}}
{{/eachIncludeParent}}

Access parent context values in your partial using {{parentContext.value}}

Upvotes: 2

Ricardo Souza
Ricardo Souza

Reputation: 16446

You can use some of the proposed solutions on the comments from the link to github:

https://github.com/wycats/handlebars.js/issues/182#issuecomment-4206666
https://github.com/wycats/handlebars.js/issues/182#issuecomment-4445747

They create helpers to pass the info to the partial.

Upvotes: 3

Related Questions