There are a good number of situations (at least in my experience) where it would be handy to be able to nest helper calls, such as capitalizing and pluralizing a word. Unless I've missed it, Handlebars doesn't seem to support doing so.
I could see the syntax being something like:
<h1>{{count}} {{capitalize {{pluralize type count}} }} Found</h1>
<span>Just click on the {{type}} you are looking for...</span>
or:
<h1>{{count}} {{capitalize pluralize(type count)}} Found</h1>
<span>Just click on the {{type}} you are looking for...</span>
Which, with _count = 5_, _type = 'person'_, and a smart enough _pluralize_ helper, would yield:
<h1>5 People Found<h1>
<span>Just click on the person you are looking for...</span>
Something like this, (or modifiers) would be nice. For now it's possible to do something like: {{capitalizePlural type count}}
You can create your own helpers and call them with something like:
Handlebars.registerHelper('capitalizePlural', function(type, count) {
type = type.charAt(0).toUpperCase() + type.slice(1)
return Handlebars.helpers.pluralize.call(this, type, count)
});
Of course, but that isn't particularly scalable.
I think this is unlikely to be supported, but I'll leave this open for now.
I plus one this
Here is a hack that works to all cascading helper calls. Basically, testing to see if the first argument is a function and if so calling it with the rest of the arguments to generate the result that this help wants to work on.
// Setup the helpers
Handlebars.registerHelper('sum', function(left, right) {
return left + right;
});
Handlebars.registerHelper('numFormat', function(num) {
// Hack around Handlebars by testing for function and using
// that to calculate the num.
if (typeof(num) == 'function') {
num = num.apply(null, Array.prototype.slice.call(arguments, 1));
}
// Do that actual work
return d3.round(num, 1);
});
// Use this template
var t = Handlebars.compile("{{ numFormat sum 1 2}} === {{ numFormat 3 }}");
assert('3 === 3', t());
+1
This is largely intentionally. For format tweaks, you can always define computed properties on your view to modify the format. One of the primary goals of Handlebars is to make the primitive syntax simple enough to make it possible for us to attach bindings without a lot of accidental mayhem.
If you look in the issue tracker, you'll see that there are still some issues related to interactions between existing helper styles (like if
and bindAttr
), and every new piece of possible syntax dramatically increases the interaction surface area.
Perhaps some day, when the existing bindings are extremely solid and mature, we can look into adding new features. For now, if it is possible to implement something via computed properties (or other existing primitives), I'm going to lean heavily in favor of using those primitives over adding new syntax.
It could be great to have, something like applying i18n to existing views, else we need to extend views and do a lot of work around.
{{view "Bootstrap.Forms.TextField" valueBinding="account.name" label={{t frontend.signup.name}} }}
If someone has nailed this problem, kindly share with me.
@trigmatic: +1
@matt001 I had tried using the thing indirectly
Handlebars.registerHelper "T", (key, options) ->
ret = options.fn( i18Label: I18n.t(key) )
{{#T frontend.signin.password}}
{{view "Bootstrap.Forms.TextField" valueBinding="view.account.password" label=i18Label type="password"}}
{{/T}}
But looks so dirty, a nested helper for this very case made sense.
@trigmatic +1
the same problem happened to me #432 and the only solution I found was compiling those nested helpers at runtime, but it seems wrong because if you nest too deep it gets ugly. Most of the problems that led me to this was likely internationalization helpers and custom classes with "if" statements.. I saw that Ember.js solve this last problem with string statements which makes smells even more.
by the way, just created a handlebars plugin for it as a workaround for now https://github.com/mateusmaso/handlebars.nested
+1
+1
I know this isn't exactly in line with the spec/best practices, but if anyone is trying to make the hack @my8bird posted work with the the newer versions of handlebars the following snippet will modify the compiler to generate compatible functions.
Handlebars.JavaScriptCompiler.prototype.lookupOnContext = function(name) {
this.push(this.nameLookup('depth' + this.lastContext, name, 'context') + ' || helpers.' + name);
};
i18n begs for this addition
+1
Great news for all that need this.
Support has been added in version 1.3.
From the docs: http://handlebarsjs.com/expressions.html
Subexpressions
Handlebars offers support for subexpressions, which allows you to invoke multiple helpers within a single mustache, and pass in the results of inner helper invocations as arguments to outer helpers. Subexpressions are delimited by parentheses.
{{outer-helper (inner-helper 'abc') 'def'}}
In this case, inner-helper will get invoked with the string argument 'abc', and whatever the inner-helper function returns will get passed in as the first argument to outer-helper (and 'def' will get passed passed in as the second argument to outer-helper.
So you can now do something like:
{{ myHelper text=(i18n "text") }}
Thanks for adding!
Most helpful comment
Great news for all that need this.
Support has been added in version 1.3.
From the docs: http://handlebarsjs.com/expressions.html
So you can now do something like:
{{ myHelper text=(i18n "text") }}
Thanks for adding!