I'm trying to implement a simple "if-equal" helper in handlebars, like so:
Ember.Handlebars.registerHelper('ifeq', function(val1, val2, options) {
return (val1 == val2) ? options.fn(this) : '';
});
And use it like so (let's assume the foo variable is set to "bar"):
{{#ifequal foo "bar"}} show something {{/ifequal}}
The problem is, when val1 is passed in, it's passed in as the string "foo", and not the actual value of the foo variable. In my debugger, I can verify that this[val1] is in fact set to the expected value of the foo variable.
Does Ember somehow alter the behavior?
Ember.Handlebars.registerHelper just passes strings in. There is a registerBoundHelper being worked on, but in your case this should work.
Ember.Handlebars.registerHelper('ifeq', function(val1, val2, options) {
return (this.get(val1) == val2) ? options.fn(this) : '';
});
Related
I'm assuming this isn't possible, but wanted to see if anyone knew any better.
With ES6, you can get the name of a function. For instance:
function foo() { return true; }
function bar() { return true; }
const functionContainer = foo;
foo.name; // 'foo'
bar.name; // 'bar'
functionContainer.name; // 'foo'
In Ember, you can pass an action into an action helper. For instance:
export default Ember.Component.extend({
actions: {
bar() {
return true;
}
}
});
And the template:
{{foo-component foo=(action "bar")}}
Within foo-component, is there some way to do this:
export default Ember.Component.extend({
doFoo: Ember.on('didRecieveAttrs', function() {
console.log(this.attrs.foo.name); // 'bar'
})
});
When I try, I just get a blank string. This makes sense, since it looks like the bar action is getting wrapped in a nameless function by ember-metal.
Anyone know of a way to grab that name? It would make a huge difference for a project I'm working on.
Nope, you can't do exactly what you want < insert technical discussion about closures here >, but you can kinda fake it by just adding another param to your component, like so:
{{foo-component foo=(action "bar") actionName="bar"}}
then in your component.js you can access
this.attrs.actionName // "bar"
I currently have a helper that looks like:
Ember.Handlebars.registerHelper('ifEq', function(a, b, opts) {
if (a == b) {
return opts.fn(this);
} else {
return opts.inverse(this);
}
});
and in my template, I do
GRAPH_TYPE: {{graphType}}
{{#ifEq graphType "p_graph"}}
TEST1
{{else}}
TEST2
{{/ifEq}}
However, this displays
GRAPH_TYPE: p_graph TEST2
This leaves me confused as there should be an exact string match above.
So, I dug into the web inspector and noticed that the value of a in the Handlebars helper was of the value graphType. Why wasn't the value passed in and how do I ensure that it is passed in?
You need to register it as a bound helper:
Ember.Handlebars.registerBoundHelper('ifEq', function(a, b, opts) {
^ like so
When you register a normal/basic helper, you're going to see the parameters passed in by string value as you're witnessing.
However, if you want the argument strings to be bound to properties in your template (which, in this case, you do), you need to use the function signature above.
The problem is setup on JSFiddle: https://jsfiddle.net/sshadmand/fNPvf/16812/
Given ...
The data passed to the template is:
{
"curr_level": 0,
"levels" : [item1, item2, item3 ...]
}
The handlebars template is:
<div>
{{#each levels}}
{{../curr_level}} == {{#index}}
{{#ifeq ../../curr_level #index}}yes{{else}}no{{/ifeq}}
{{/each}}
</div>
The #ifeq helper function is:
Handlebars.registerHelper('ifeq', function(val1, val2, options) {
console.log("if equal", val1, typeof(val1), val2, typeof(val2));
if (val1 == val2){
return options.fn(this);
}
return options.inverse(this);
});
The problem is:
Accessing the parent curr_level variable using ../ works to print the value to the screen, but when the curr_level variable is sent to the helper it is undefined. I have tried passing different depths to the helper e.g. curr_level, ../curr_level, ../../curr_level etc
For all depths "val1" in the helper is undefined. Again, I am able to access the curr_level variable when it is not using the comparison helper.
Note: This is similar to, but very different from this SO question asking about parents. The main difference being I am trying to pass the parent variable to a helper function:
Access properties of the parent with a Handlebars 'each' loop
Let's say, I would like to call an Ember Handlebars helper from an emblem.js template.
I have declared the helper through (CoffeeScript syntax):
Ember.Handlebars.registerHelper 't', (key, value, context) ->
...
When attempting to invoke the helper from emblem using
= t "my.i18n.key", "val", count: 42
key is assigned correctly, all but the first arguments are dropped and the second argument is replaced by some options hash as would be the case for a bound helper (the third argument is undefined).
Is there any way to invoke a helper in emblem.js with more than a single argument?
I think you can only pass one value, but you can use the options hash with many bound/computed properties like this:
Ember.Handlebars.registerBoundHelper('truncatedQuery',
function truncatedQueryHelper(value, options) {
console.log('value', value, 'options:',
options.hash['key1'], options.hash['key2'], options.hash['key3']);
// do work...
return somethingUseful;
});
And in your template use optionsHashKeyBinding=propertyOnControllerName like this:
<div class='truncated-query'>
{{truncatedQuery 'value' key1Binding=prop1 key2Binding=prop2 key3=42}}
</div>
Where prop1 and prop2 are on the controller:
App.IndexController = Ember.Controller.extend({
prop1: 'foo',
prop2: function() {
return this.get('prop1') + 'bar';
}.property('prop1'),
});
For instance, is there a way to nest my "i18n" helper inside another helper's hash variable?
{{view "SearchView" placeholder="{{t 'search.root'}}" ref="search" url="/pages/search" className='home-search' polyfill=true}}
Update: Handlebars now supports subexpressions, so you can just do:
{{view "SearchView" (t 'search.root')}}
Your scenario is not directly supported, but there a couple of workarounds you can use. The handlebars helpers are just javascript code, so you can execute them from within the helper code itself:
function translateHelper() {
//...
}
function viewHelper = function(viewName, options) {
var hash = options.hash;
if(hash.placeholder) {
hash.placeholder = translateHelper(hash.placeholder);
}
};
Handlebars.registerHelper('view', viewHelper);
Handlebars.registerHelper('t', translateHelper);
And just pass the i18n key to as the argument:
{{view placeholder="search.root"}}
This is nice, as long as your helper knows which arguments should be localized, and which not. If that is not possible, you can try running all the helper arguments through Handlebars, if they contain a handlebars expression:
function resolveNestedTemplates(hash) {
_.each(hash, function(val, key) {
if(_.isString(val) && val.indexOf('{{' >= 0)) {
hash[key] = Handlebars.compile(val)();
}
});
return hash;
}
function view(viewName, options) {
var hash = resolveNestedTemplates(options.hash, this);
}
And use the nested template syntax you described:
{{view placeholder="{{t 'search.root'}}" }}
I realize neither of these options are perfect, but they're the best I could think of.