Handlebars.js: block helper adds extra level to parent context (../..)

Created on 27 Feb 2012  路  11Comments  路  Source: handlebars-lang/handlebars.js

It seems you have to use ../.. to get to the parent context from inside of a block helper. I'm not sure if this is correct or not. It feels like the parent context should stay the same (..), similar to how the context stays the same (e.g. "block helper staying in the same context" in qunit_spec.js). Here's a runnable html example in case I'm using the wrong terminologies and not making sense:

<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="http://cloud.github.com/downloads/wycats/handlebars.js/handlebars-1.0.0.beta.6.js"></script>
<script id="t" type="text/x-handlebars-template">
{{#subitems}}
  name: {{name}}<br>
  parent id: {{../id}}<br>
  {{#h}}
    (works) name: {{name}}<br>
    (doesn't work) parent id: {{../id}}<br>
    (works) parent parent id: {{../../id}}<br>
  {{/h}}
{{/subitems}}
</script>
</head>
<body>
<div id="d"></div>
<script type="text/javascript">
  var f = Handlebars.compile($('#t').html())
  Handlebars.registerHelper('h', function(options) {
    return options.fn(this)
  })
  var items = { 'id': 123, 'subitems': [{'name': 'abc'}]}
  $('#d').html(f(items))
</script>
</body>
</html>

Thanks,
Trask

bug

Most helpful comment

You can just do @root.<id or other ctx item> for anyone that stumbles on this.

All 11 comments

This is (presumably) the same as issue #201.

I have the same issue with 1.0.beta.6

ditto what cj said.

This isn't really a bug. Let me try to explain:

In Handlebars, each template has a context. Blocks ({{#if foo}}) create inline templates, and the helper (if) decides what context to render the template in.

Some helpers preserve the current context (like if), while others change it in different ways (each and with, for example). Helpers can even change the context to something totally new if they want (options.fn({ zomg: true })).

The .. feature means "look this up on the context of the parent template". In some cases, that will amount to the parent in the current object (with). In other cases, it will not. In cases like if, which preserve the context, .. will happen to point to the same object, but this is just a coincidence.

In all cases, .. is lexically bound to the parent template.

@wycats : Can you explain a little bit more on the resolution of parent context incase of #with:
"In some cases, that will amount to the parent in the current object (with)"
My understanding for var items = { 'id': 123, 'subitems': [{'name': 'abc'}], 'inventory': {'available': 5, 'sold':4}}

if - does not create a new context as its just an if condition ?

each subitems - creates a new context for each subitem and a ../id will work fine as it takes it to the parent context

{{#with inventory}}
id works here {{../id}}
{{#if available > 2}}
id does not work {{../id}}
id does work {{../../id}}
{{/if}}
{{/with}}
#with - creates a new context and ../id to refer to the parent context, but a #if within a #with behaves like a nested context since it requires a ../../id.

Ideally I would expect #if to behave the same at top level and within #with, but the context seems to be different!
So #if within any block like #each or #with behaves differently than at the top level. Why?

@sharans - I'm confused about this as well. I have an SO question related to it, but haven't gotten any response. Seems {{#if}} _doesn't_ preserve the parent context in some situations, but not sure why or when to expect it...

@jt000 There is no preserving of the parent context. ../ means use the context of the block above me. When calling if you create a new block, thus need another ../, even if the execution context is not changed. I can't speak to the docs for other projects but you might want to speak with them if they are confusing.

@kpdecker - Thanks. That helps a lot. I just gave this a try in jsFiddle using the latest handlebars & it works as I originally expected (_#if block context is same as parent block context_). I'm not sure why it is not behaving the same in my code using assemble.io. I'll have to look into that further. Thanks again.

wait what you need a helper to access the scope? That's super annoying and makes me regret choosing to use handlebars if true

You can just do @root.<id or other ctx item> for anyone that stumbles on this.

Correct me if I'm wrong, but I'm pretty sure that instead of calling options.fn or options.inverse with this, you can just call it with the root of the data that is passed to the template, so you "avoid" creating a new context.

Handlebars.registerHelper("is",   (value, test, options) => value === test ? options.fn(options.data.root) : options.inverse(options.data.root));
Handlebars.registerHelper("isnt", (value, test, options) => value !=  test ? options.fn(options.data.root) : options.inverse(options.data.root));
Handlebars.registerHelper("gt",   (value, test, options) => value  >  test ? options.fn(options.data.root) : options.inverse(options.data.root));
Handlebars.registerHelper("gte",  (value, test, options) => value >=  test ? options.fn(options.data.root) : options.inverse(options.data.root));
Handlebars.registerHelper("lt",   (value, test, options) => value  <  test ? options.fn(options.data.root) : options.inverse(options.data.root));
Handlebars.registerHelper("lte",  (value, test, options) => value  <= test ? options.fn(options.data.root) : options.inverse(options.data.root));
Handlebars.registerHelper("or",   (a, b, options)        =>     a ||  b    ? options.fn(options.data.root) : options.inverse(options.data.root));
Handlebars.registerHelper("isNull",   (value, options)   => value === null ? options.fn(options.data.root) : options.inverse(options.data.root));

This wouldn't work inside any other block helpers where scope is meant to be modified (e.g. #each or #with), but you get the idea.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

asgraf picture asgraf  路  5Comments

sontek picture sontek  路  3Comments

morgondag picture morgondag  路  5Comments

fcpauldiaz picture fcpauldiaz  路  4Comments

ustun picture ustun  路  6Comments