How to use Meteor methods inside of a template helper


How can I define a Meteor method which is also callable in a template helper?

I have these two files:

file: lib/test.js

Meteor.methods({
    viewTest : function (str) {
        return str;
    }
});

file: client/myView.js

Template.helloWorld.helpers({
    txt : function () {
        var str = Meteor.call('viewTest', 'Hello World.');
        return str;
    }
});

When I give "str" a normal string everything works fine. But in this case my template does not get any value. I defined - for the test - in the same file where the method is a normal function and tried to call the function. The error I got was that the function does not exist. So I think that Meteor tries to render the template before it knows anything about the methods I defined for it. But I think that this is a bit unusual - isn't it?

There is now a new way to do this (Meteor 0.9.3.1) which doesn't pollute the Session namespace

Template.helloWorld.helpers({
    txt: function () {
        return Template.instance().myAsyncValue.get();
    }
});

Template.helloWorld.created = function (){
    var self = this;
    self.myAsyncValue = new ReactiveVar("Waiting for response from server...");
    Meteor.call('getAsyncValue', function (err, asyncValue) {
        if (err)
            console.log(err);
        else 
            self.myAsyncValue.set(asyncValue);
    });
}

In the 'created' callback, you create a new instance of a ReactiveVariable (see docs) and attach it to the template instance.

You then call your method and when the callback fires, you attach the returned value to the reactive variable.

You can then set up your helper to return the value of the reactive variable (which is attached to the template instance now), and it will rerun when the method returns.

But note you'll have to add the reactive-var package for it to work

$ meteor add reactive-var

Sashko added a neat little package called meteor-reactive-method to solve this problem.

$ meteor add simple:reactive-method
Template.helloWorld.helpers({
  txt: function() {
    return ReactiveMethod.call('viewTest', 'Hello World.');
  }
});

As I point out in common mistakes, helpers should be side-effect free, so I'd use this technique with caution. However, it's a really handy shortcut for cases where:

  • The helper should fire only once (it doesn't depend on reactive state).
  • The invoked method doesn't mutate the database.

You need to interface your return value with a Session variable as the request is asynchronous:

Template.helloWorld.helpers({
    txt : function () {
        return Session.get("txt") || "Loading";
    }
});

Template.helloWorld.created = function() {
    Meteor.call('viewTest', 'Hello World.', function(err, result) {
        Session.set("txt", result);
    });

}

So .rendered should be called once when your template loads (at least it should with the newer version of Meteor.)

The value would be called and displayed. Otherwise it would say "Loading".


Methods on the client side are asynchronous, and their return value is always undefined. To get the actual value returned by the method, you need to provide a callback:

Meteor.call('method', 'argument', function(error, result) {
    ....
});

Now, there's no easy way to use the result in your helper. However, you can store it in your template as a data object and then return it in the helper:

Template.template.created = function() {
    var self = this;
    self.data.elephantDep = new Deps.Dependency();
    self.data.elephant = '';
    Meteor.call('getElephant', 'greenOne', function(error, result) {
        self.data.elephant = result;
        self.data.elephantDep.changed();
    });
};

Template.template.showElephant = function() {
    this.elephantDep.depend();
    return this.elephant;
};

This is expected behavior. You are not using methods as they are intended.

Your code defines a server method viewTest and a corresponding method stub on the client with the same name.

Meteor.call('viewTest', 'Hello World.'); remotely calls viewTest on the server and in parallel runs the stub on the client.

Regarding the return value of the stub please see the documentation here, in particular:

On the client, the return value of a stub is ignored. Stubs are run for their side-effects: they are intended to simulate the result of what the server's method will do, but without waiting for the round trip delay.

Regarding the return value of the server method please see the documentation here, in particular:

On the client, if you do not pass a callback and you are not inside a stub, call will return undefined, and you will have no way to get the return value of the method. That is because the client doesn't have fibers, so there is not actually any way it can block on the remote execution of a method.


There is a fine little package for this by @msavin:
https://atmospherejs.com/msavin/fetcher