Let's say I have a template which iterates over a collection of items, and I want to call a function with each item which is specific to the controller, and not a model-level concern:
{{#each people as |person|}}
icon name: {{findIconFor(person)}}
{{/each}}
I'd like to define findIconFor
in the controller, because this is something specific to this particular view.
export default Ember.Controller.extend({
findIconFor: function(person) {
// figure out which icon to use
}
);
But that doesn't work. The template fails to compile. Parse error: Expecting 'STRING', 'NUMBER', 'ID', 'DATA', got 'INVALID'
What is the "ember way" to do this?
I'd use a computed property in the controller:
iconPeople: Ember.computed('people.@each', function(){
var that = this;
return this.get('people').map(function(person){
return {
'person': person,
'icon': that.findIconFor(person)
};
});
})
Now you could get the icon from {{person.icon}}
and the name from {{person.person.name}}
. You might want to improve on that (and the code is untested), but that's the general idea.
As i spent almost entire day on a similar problem here is my solution.
Because Ember for some reason just doesn't allow you to run a controller functions directly from the template (which is ridiculous and ties your hands in some very stupid ways and i don't know who on earth decided this is a good idea ...) the thing that makes most sense to me is to create an universal custom helper, that allows you to run functions from the template :) The catch here is that you should always pass the current scope (the "this" variable) to that helper.
So the helper could be something like this:
export default Ember.Helper.helper(function([scope, fn]) {
let args = arguments[0].slice(2);
let res = fn.apply(scope, args);
return res;
});
Then, you can make a function inside your controller, that you want to run, for example:
testFn: function(element){
return element.get('name');
}
and then in your template you just call it with the custom helper:
{{#each items as |element|}}
{{{custom-helper this testFn element}}}
{{/each}}
The first two arguments to the helper should always be "this" and the name of the function, that you want to run, and then you can pass as many extra arguments as you wish.
Edit: Anyway, every time when you think you need to do this, you should think if it will not be better to create a new component instead (it will be in 90% of the cases)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With