Dynamically insert helper names


#1

Hey!

I am creating a statistics dashboard, which means a lot of tables. I want to save all the different statistics and their respective helper in an array. When I insert the table rows dynamically I get error saying

Can't call non-function: newClients

HTML

			<tbody>
			{{#each statistics}}
			<tr>
				<td>{{name}}</td>
				<td>{{method 5}}</td>
				<td>{{method 4}}</td>
				<td>{{method 3}}</td>
				<td>{{method 2}}</td>
				<td>{{method 1}}</td>
				<td>{{method 0}}</td>
			</tr>
			{{/each}}
		</tbody>
Template.clients_widget.onCreated(function(){
  this.statistics = [
    {name:"New Clients", method:"newClients"},
    {name:"Total Clients", method:"totalClients"},
    {name:"Active(task) Clients", method:"taskClients"},
    {name:"Active(login) Clients", method:"loginClients"},
  ];
});
Template.clients_widget.helpers({

  statistics: function(){
    return Template.instance().statistics;
  },

  newClients: function(month){
    var certainMonth = Template.instance().certainMonth(month);
    var clients = [];

    Template.instance().clients.forEach(function(c){
      if(c.createdAt.getMonth() === certainMonth ){
        clients.push(c);
      }
    });

    return clients.length;
  },
});

#2

newClients is a string and not a function, therefore you cannot invoke it.

What you may do is create a “meta helper” that takes in two arguments, first of which is the name of the function you want to invoke.


#3

I was afraid i needed to do that. As far as I am aware there is on way to call helpers from other helpers right? REFACTOR ALL THE THINGS i guess…


#4

Ok for anyone interested: It was actually quite straight forward:

  1. I copy/pasted all my helper functions into an object bound to the template instance in the onCreated call

  2. I created a meta helper that calls the appropriate function like so

    calculation: function(method,month){
    return Template.instance().calcsmethod;
    }


#5

It does not seem like you need a huge refactor.

The problem is, you are trying to do this:

var someFunction = function(){ /*do something*/};
var someVar = "someFunction";

//this won't work
someVar();

//but this would
window[someVar]();

and if you need to pass in args, you could use apply

So basically, you need to create one helper and pass in both your
method name and your args to that helper which converts your string
method name into its appropriate function counterpart.


#6

You’ve beaten me to it, kudos! :smile:


#7

This is how I have always called helpers via another Javascript function

Template.name.__helpers.get('helperName')(/*params*/)

I hope this helps!