Meteor method returns correct value on server but is Undefined on Client

Howdy.

Probably kind of trivial, but basically, I’m returning the count of documents from a collection that match two values (an ID association and a boolean (active).

There’s three schemas this is working with. “Run”, “RunChallenge”, and “ChallengeQuestion”.

The template helper:

  questionCount: function() {
    var runSlug = FlowRouter.getParam('runSlug'),

    runId = Run.findOne({
      runSlug: runSlug
    })._id,

    runChallengeId = RunChallenge.findOne({
      runChallengeToRun: runId
    })._id,

    theCount = Meteor.call('countQuestions', runChallengeId, function(error, result) {
      return result;
    });
    console.log(theCount);

  }

The server method:

Meteor.methods({
  countQuestions: function countQuestions(challengeQuestionTo) {
    var questionCount = ChallengeQuestion.find({
      challengeQuestionTo: challengeQuestionTo,
      active: true
    }).count();
    
    var returnedQuestionCount = questionCount.toString();
    console.log(returnedQuestionCount + " - " + typeof returnedQuestionCount);
  }
});

So, the server console is logging correctly:
I20170410-08:42:30.834(-4)? 4 - string

But the browser console is getting:
Exception in template helper: TypeError: Cannot read property '_id' of undefined
and also the console.log(theCount) output is returning undefined.

I’ve narrowed down the exception in Template Helper being related to the var runChallengeId, but can’t seem to figure either of these out.

Is there something I’m doing wrong in practice? I’ve snubbed out similar things like this before to retrieve counts of docs, but not where its crossing relationships.

Suggestions?

Thanks!

So, if am not mistaken, you are missing a return statement in your method. Something like return returnedQuestionCount.
And if you move the console.log(theCount) two lines up (in the callback) it should give you the result.
Furthermore, you should always check with a simple if if there is a runId. For example:

var run = Rund.findOne({runSlug: runSlug});
if (run) {
  // continue here
}

Oh, and you should check if runSlug is defined

1 Like

Thanks for the quick reply and help @jhuenges!

I had my head wrapped in this too long and was re-arranging things and caused more problems. But I fixed it.

I also simplified the task into two helpers, and wrapped the template into a {{#with}} block:

    var runSlug = FlowRouter.getParam('runSlug');

    var runId = Run.findOne({
      runSlug: runSlug
    })._id;

    var runChallenge = RunChallenge.findOne({
      runChallengeToRun: runId
    });

    return runChallenge;
  },

  questionCount: function() {
    var runChallengeId = this._id;

    Meteor.call('countQuestions', runChallengeId, function(error, result) {
      if (error) {
        Bert.alert(error.reason, "warning");
      } else {
        Session.set('questionCount', result);
      }
    });
    return Session.get('questionCount');
  }```

Not sure if this is a good practice using Session.set/Session.get (I feel like this is "old-school Meteor"), but it's working OK. The only occurrence of these two things will happen on a specific route/template, but yes -- I'll wrap it up in if's after declaration to avoid problems.