Method stubs and Template instances


#1

After years working with Blaze, I was never able to figure out how to access a template instance inside a method call from the method stub. A quick refresher from the docs

If you do define a stub, when a client invokes a server method it will also run its stub in parallel. 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. If a stub throws an exception it will be logged to the console.

and then I suddenly had this epiphany and thought I share it.

Here is a usual Template.onCreated where I call a method getSecureString, in the simulation I want to be able to pre-set the result while the server part of the method is doing it’s job. As an example I defined the method to take the parameter nameOfReactiveVar to set to the simulated value. In the simulated portion of the method I can access the current template instance via Blaze.currentView.templateInstance() and everything that is defined within that instance

Template.hello.onCreated(function helloOnCreated() {
  var instance = this;
  instance.secureString = new ReactiveVar('');
  Meteor.call('getSecureString', 'secureString' , (error, secureString) => {
    if (error) {
      console.error(error.message);
    } else {
      instance.secureString.set(secureString);
    }
  });
});

Here is the definition of the method, it exists both on the client (isSimulation) as well as on the server, so when the client calls this method the simulation runs

import { Random } from 'meteor/random';

Meteor.methods({
  getSecureString(nameOfReactiveVar) {
    if(this.isSimulation) {
      //
      // we are still on the client, find out what template instance called us
      //
      const instance = Blaze.currentView.templateInstance();
      //
      // check if the instance has a ReactiveVar nameOfReactiveVar
      //
      if(instance && instance[nameOfReactiveVar] != undefined && instance[nameOfReactiveVar] instanceof ReactiveVar) {
        instance[nameOfReactiveVar].set(`calling server, please wait ... ${Random.id()}`);
      }
    } else {
      //
      // this is happening on the server
      //
      this.unblock();
      let secureString = 'No user logged in. Please login in user and try again.';
      // make the method delay for 5 seconds
      Meteor._sleepForMs(5000);
      if (this.userId != null) {
        secureString = `This is from the server: ${Random.id()}`;
      }
      return secureString;
    }
  },
})

#2

To me, this feels like a huge violation of separation of concerns. What’s wrong with doing it the usual way?


#3

What do you think is the violation of concerns and the usual way?


#4

I’d probably just set that in the template from where I call the method, e.g., from within the created block:

...
this.myReactiveVar.set(`calling server, please wait ... ${Random.id()}`);
Meteor.call("nameOfReactiveVar", (err, res) => {
   ...
  this.myReactiveVar.set(res);
})
...

Something like that. If I found myself calling that frequently, I might extract the entire thing to a function which I call

function getSecureString() {
   this.myReactiveVar.set(`calling server, please wait ... ${Random.id()}`);
   Meteor.call("nameOfReactiveVar", (err, res) => {
      ...
     this.myReactiveVar.set(res);
   })
}

...
onCreated(function() {
  this.myReactiveVar = new ReactiveVar();
  getSecureString.call(this);
});
...

#5

As in the template should be the only place interacting with the DOM that it renders, and the only place modifying it’s internal variables.
Methods should only worry about it’s specific task (in this case, fetching data from the server)

Looking at it another way, If I picked was reading the code, it would be much easier for me to understand what is happening if all changes to to secureString happen inside the file where it is used.

If I wanted to change the loading message, I would start by opening the file for that template and quickly discover that the string is not being set there.


#6

Those are valid points. For the case of latency compensation the method stub on the client needs to be able to interact with assets on the client, and I have always tried to figure out how