Knowing how Meteor decides to recompute my template helper

Trying to grasp Meteor's reactivity has been quite the journey. The concept of it triggering a page re-render when a reactive data source in a template changes makes sense to me. I've also got a good handle on what qualifies as a reactive source - things like Session and MongoDB cursors.

However, I've hit a roadblock when it comes to understanding these mysterious "behind the scenes" calls to my template helpers. There seems to be more than just reactivity at play here.

In particular, within the code snippet below, there's a helper called friendRequests that sometimes gets recomputed twice or even thrice during a single visit to the /friends page. Oddly enough, if it recalculates twice, the database queries work fine! But with three recalculations, the first DB access fails for some reason while the subsequent two succeed.

During the failure, this stack trace is revealed:

// NOTE: imsolonely is one of the users that should be returned in the friendRequests
imsolonely's public key: undefined
debug.js:41 Exception in template helper: TypeError: Cannot read property 'profile' of undefined
    at Object.Utils.getPublicKeyByUsername (http://localhost:3000/lib/utils.js?acf4e03d4c8a70819c26f8d2fd08caf7100768fe:79:22)
    at Object.Utils.getFingerprintByUsername (http://localhost:3000/lib/utils.js?acf4e03d4c8a70819c26f8d2fd08caf7100768fe:88:24)
    at http://localhost:3000/client/friends.js?dbec4a7537c9d0abf56a74489824969cb7baadfe:25:35
    at Array.forEach (native)
    at Function._.each._.forEach (http://localhost:3000/packages/underscore.js?0a80a8623e1b40b5df5a05582f288ddd586eaa18:156:11)
    at Object.Template.friendRequests.helpers.friendRequests (http://localhost:3000/client/friends.js?dbec4a7537c9d0abf56a74489824969cb7baadfe:22:7)
    at http://localhost:3000/packages/blaze.js?77c0809654ee3a10dcd5a4f961fb1437e7957d33:2693:16
    at http://localhost:3000/packages/blaze.js?77c0809654ee3a10dcd5a4f961fb1437e7957d33:1602:16
    at Object.Spacebars.dot (http://localhost:3000/packages/spacebars.js?3c496d2950151d744a8574297b46d2763a123bdf:231:13)
    at http://localhost:3000/client/template.friends.js?a2da726f6dad1aaecfdedfe216aa3378fff938b5:24:37
utils.js?acf4e03d4c8a70819c26f8d2fd08caf7100768fe:78 imsolonely's public key: {"profile":{"publicKey":"0404b4880129edc1ea2652dd1eff1c8728269874b9b0ace02cc90edcb449c3f3d716c2f8b79a5fe5695d52cd85aed228f977073538625e8e71f1cfd764766669b1"},"_id":"sXjzt7YHA8KTyAib5"}
utils.js?acf4e03d4c8a70819c26f8d2fd08caf7100768fe:78 imsolonely's public key: {"profile":{"publicKey":"0404b4880129edc1ea2652dd1eff1c8728269874b9b0ace02cc90edcb449c3f3d716c2f8b79a5fe5695d52cd85aed228f977073538625e8e71f1cfd764766669b1"},"_id":"sXjzt7YHA8KTyAib5"}

This issue extends to several, if not all, of my helpers. They're being called even when they shouldn't be, especially when the user isn't logged in (and therefore, the helper shouldn't need to recalculate).

Take a look at the relevant code snippets:

client/friends.js:

Template.friendRequests.helpers({
  friendRequests: function () {
    // Problem 1: The template gets called right after I log in but before I am fully logged in
    // so I need this call here.
    if(!Meteor.user())
      return Utils.notLoggedInErrorMsg;

    // Problem 2: The template gets called three times. The first time fails the DB query
    // as if the DB row did not exist. The next 2 times it succeeds. But it should only be
    // called once.
    var reqs = Friends.getFriendRequests(Utils.me());

    _.each(reqs, function(element, it, list) {
      check(element.initiator, String);
      // Get the user's fingerprint
      element.fingerprint = Utils.getFingerprintByUsername(element.initiator); 
    });

    return reqs;
  },  
});

client/friends.html:

<template name="friends">
  {{> friendRequests}}
  {{> searchForm}}

  <h2>My friends</h2>
  <ul>
  {{#each friends}}
    <li>{{this}}</li>
  {{/each}}
  </ul>
</template>

<template name="friendRequests">
  <h2>Friend requests</h2>
  {{#if friendRequests.length}}
    <p>Someone's popular today!</p>
    <ul>
    {{#each friendRequests}}
      <li><b>{{initiator}}</b> with fingerprint <pre style="display: inline">{{fingerprint}}</pre> sent   you a request on <em>{{date}}</em>. <a href="#">Accept <b>{{initiator}}</b> as a friend?</a></li>
    {{/each}}
    </ul>
  {{else}}
    <p>Sorry, nobody likes you right now.</p>
  {{/if}}
</template>

lib/utils.js:

Utils = {
  // ...
  // other stuff
  // ...

  // @return a hex-encoded public key as a string
  getPublicKeyByUsername: function (username) {
    var user = Meteor.users.findOne({ username: username }, { fields: { 'profile.publicKey': 1 } }); 
    console.log(username + '\'s public key: ' + EJSON.stringify(user));
    var pubKey = user.profile.publicKey;

    return pubKey;
  },  

  // NOTE: not used yet, i used the CryptoUtils function directly when I needed it
  //  
  // @return the fingerprint as a hex-encoded string
  getFingerprintByUsername: function (username) {
    var pubKey = Utils.getPublicKeyByUsername(username);

    var fingerprint = CryptoUtils.getPublicKeyFingerprint(pubKey);

    return fingerprint;
  },  

  notLoggedInErrorMsg: 'Meteor is being silly and calling this when I\'m not logged in.',
}

If relevant, I'm utilizing the iron:router 1.0.3 package to navigate to the /friends URL.

Any insights into why the friendRequests helper keeps recalculating even when it's out of sight, and why it's triggered multiple times - rather than just once - upon refreshing the /friends page would be highly appreciated!

Sincerely, Alin

Answer №1

Expect that your helpers will be called multiple times, especially if the template is being rendered while the user is logging in and before the data is published:

  1. Called when the route is requested
  2. Executed upon user login completion
  3. Triggered when Friends data first arrives on the client
  4. Run again when additional or modified Friends data is available

To address the initial issue, you can use

Meteor.user() || Meteor.loggingIn()
as demonstrated in my answer to this question. For the second problem, refer to my article on guards, where it's advised not to assume that all data has been received by the client. You must either verify its existence or explicitly wait for the subscription in your router before rendering the template.

Answer №2

Upon further investigation, I discovered an issue within my waitOn hook. Instead of returning anything, I was simply making individual Meteor.subscribe() calls without returning them as an array. The following code snippet resolved the DB exceptions:

Router.configure({
  layoutTemplate: 'mainLayout',
  loadingTemplate: 'loading',
  waitOn: function () {
    return [
      Meteor.subscribe('userData'),
      Meteor.subscribe('allUserData'),
      Meteor.subscribe('UserProfiles'),
    ];
  },
});

I am still grappling with finding a suitable solution for the undefined Meteor.user(). Perhaps this adjustment has also addressed that issue?

PS: Special thanks to @DavidWeldon for assisting me in pinpointing and troubleshooting this problem!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to utilize JSON.stringify for substituting all keys and values?

In my current project, I am exploring how to leverage the replacer function argument within JSON.Stringify in JavaScript to alter the word case (toUpper / toLower case). The challenge I am facing is that my JSON data is not simply key:value pairs; some val ...

What is the best way to ensure that a link fragment scrolls to the top of the page in Angular?

Having trouble with link fragments in my Angular single-page-app: <a href="/#/search">Search</a> When clicking this link, it takes me to the right page but keeps my scroll position. I want it to scroll to the top of the page so I'm curre ...

Is there a way to set up a local npm module directory without using symlinks

Here is a breakdown of the file structure in a simple way. /my-module ..package.json /my-app ..package.json I am trying to have my-app install my-module locally. This is what I have attempted: "dependencies": { "myModule": "../my-module" } The opti ...

Is there a way to dynamically toggle the visibility of a floating textarea between on and off?

I have my own blog website: Essentially, what I am aiming for is When a user clicks on the search button, a floating semi-transparent textarea window should appear inside the designated rectangle area (as shown in the image, that red orange rectangle). ...

Use jQuery to trigger a click event when an element is in focus, unless it was clicked to

I am currently developing a website using the MDL framework. One issue I encountered is that there is no default select form element provided. After some research, I found a solution by utilizing a menu component that displays when the input is clicked. Th ...

Troubleshooting focus loss in React input fields within a Datatable component implemented in a

I noticed an issue with my Datatable where the input field loses focus whenever I type a character and define the component inside another component. Surprisingly, when I directly place the component in the datatable, it works perfectly fine. I understand ...

I'm having trouble sending registration emails through my server. What could be causing this issue?

Currently, I am in the process of developing a registration system that automatically sends an email with the user's username and password once they have successfully registered. The registration process functions smoothly up until the point where the ...

Creating seamless scrolling in ReactJS without any pre-built components

Can anyone guide me on how to implement an infinite scroll in reactJs using a JSON dataset? I prefer building it from scratch without relying on any library. {data?.map((conto) => { return ( <Suspense key={conto._uid} fallback={<p>Loadin ...

Tips for showcasing a drop-down menu using Jquery

I am currently utilizing jQuery to showcase a drop-down menu. It is successfully working for a single menu and displaying the appropriate drop-down. However, when I attempt to use more than one menu, it displays all of the drop-down menus simultaneously. I ...

Error during compilation in npm (symbol '_' is not recognized)

After updating all the dependencies in my JavaScript program without making any changes to my components, I encountered an error when running: npm run build The error specifically mentions a problem with one of my components: Failed to compile. ./src/c ...

WebView no longer refreshes when the document location is updated

I currently have a basic HTML/JavaScript application (without Phonegap) that I utilize alongside a native Android app and a WebView. When certain situations arise, I need to reload the current page in the JavaScript portion. On my old phone with Android 4 ...

Update the contents of a neighbor div within the same parent element using jQuery

Attempting to set the range slider value within the slider parent sibling var updateRangeSlider = function() { var slider = $('.range-slider'), range = $('.range- value = $('.range-slider__value'); sli ...

Grunt is throwing an error message of "Cannot GET/", and unfortunately ModRewrite is not functioning properly

I've recently started using Grunt (just began last Friday). Whenever I run Grunt Serve, it displays a page with the message "cannot GET/" on it. I tried implementing the ModRewrite fix but the error persists. Any assistance would be highly appreciat ...

What is the simplest method to check for the presence of a value within an object when utilizing lodash or angularjs?

I'm encountering an issue where this code works perfectly if "obj" is a collection, but falls short when trying to determine if a value exists within a single object. What would be the most efficient approach, utilizing either lodash or AngularJS, to ...

Mastering the Art of Crafting an Effortless Slide Showcase

I have created a carousel that works fine except for one issue: when I reach the last image or go back from the first image, I want the image to be displayed. For example, when you click the right arrow after reaching the last image, it should show the fir ...

Converting a text area into a file and saving it as a draft in the cloud with the

Can content from a text area be converted into a file of any chosen format and saved in the cloud? Additionally, should every modification made in the text area automatically update the corresponding file stored in the cloud? ...

Chrome successfully handles cross-domain AJAX calls with Windows authentication, whereas Firefox encounters issues with the same functionality

I am facing an issue with my WCF service that uses windows authentication. When I call this service using ajax in Google Chrome, everything works perfectly as the credentials are cached. However, in Firefox, I am receiving a 401 unauthorized error. I would ...

AngularJS $scope variable is not defined during page load

Experiencing difficulties retrieving service data to include in the variable's scope. Below is my controller code: 'use strict'; var app = angular.module('ezcms2App.controllers', []); app.controller('NoticiaCtrl', [&apo ...

Random Failures in Executing MapReduce Queries in MongoDB

In my Rails 3.1 application, which runs on Ruby 1.9.2 and MongoDB 2.0.2 using Mongoid as an ODM, I am encountering a peculiar issue. I have set up a MapReduce query to run multiple times on a single page load. Initially, the query works fine in development ...

Implementing a Javascript solution to eliminate the # from a URL for seamless operation without #

I am currently using the pagepiling jQuery plugin for sliding pages with anchors and it is functioning perfectly. However, I would like to have it run without displaying the '#' in the URL when clicking on a link like this: www.mysite.com/#aboutm ...