Global variables in Meteor

I have

var Schemas = {};

Meteor.isClient && Template.registerHelper("Schemas", Schemas);

Schemas.Person = new SimpleSchema({
  fullName: {
    type: String,
    index: 1,
    optional: true,
  },
  email: {
    type: String,
    optional: true
  },
  address: {
    type: String,
    optional: true
  },
  isActive: {
    type: Boolean,
  },
  age: {
    type: Number,
    optional: true
  }
});

in one file and

var Collections = {};

Meteor.isClient && Template.registerHelper("Collections", Collections);

Persons = Collections.Persons = new Mongo.Collection("Persons");
Persons.attachSchema(Schemas.Person);

in another file.

I get the error ReferenceError: Schemas is not defined. It's rather obvious that I have to define Schemas in my collections.js file instead of having them separate. But how does Meteor work with code in separate files? I can access some objects and variables while others are unaccessible.


Solution 1:

When you define a variable in the classic JavaScript way :

var someVar = 'someValue';

at the root of your .js file Meteor scopes it to the file using an IIFE.

If you want to define a global variable, simply don't write the var, giving :

someVar = 'someValue';

This will define a variable in all your application by default, although you may restrict it by writing that declaration in a specific recognized folder (client or server folder for example).

However this variable won't be defined absolutely first. It will be defined when Meteor runs the actual code that defines it. Thus, it may not be the best practice because you're going to struggle with load order, and it will make your code dependent on how Meteor loads files: which folder you put the file in, the name of the file... Your code is prone to messy errors if you slightly touch your architecture.

As I suggested in another closely related post you should go for a package directly!

Solution 2:

Variables in Meteor declared with the var keyword are scoped to the file they are declared in.

If you want to create a global variable do this

Schemas = {}

Solution 3:

ReferenceError is a Node error. Meteor is a framework on top of Node.

Node has a global scope (aka Node's global variable). This error is thrown by Node (not Meteor) if you try to access an undefined global variable.

Browsers also have a global scope called window, and do not throw ReferenceErrors when undefined variables are accessed.

Here's a pattern I like for adding functionality to a class (it's very Meteor):

/lib/Helpers.js      <-- Helpers for everyone (node+browser)
/server/Helpers.js   <-- Server helpers (node)
/client/Helpers.js   <-- Client helpers (browser)

Consider these implementations:

// /lib/Helpers.js
Helpers = {/* functions */};  // Assigned to window.Helpers and global.Helpers

// /server/Helpers.js
Helpers = _.extend(Helpers, {/*more functions*/}

// /client/Helpers.js
Helpers = _.extend(Helpers, {/*more functions*/}

This is a trivial example. What if I didn't want to worry about load order? Why not _.extend() in /lib/Helpers.js?

// /lib/Helpers.js
// Helpers = {/* functions */};                  // Overwrites...
Helpers = _.extend(Helpers, {/* functions */});  // ReferenceError

Because you'll get a ReferenceError from Node if Helpers isn't defined - specifically the "Helpers" used as an argument. (Node knows to assign Helpers as global.Helpers).

Here are two ways to "fix" this:

1) Assign Helpers to something

// /lib/Helpers.js
// Helpers = Helpers || {}    // would be another ReferenceError
if (typeof Helpers === 'undefined') Helpers = {};
Helpers = _.extend(Helpers, {/* functions */});

2) Use helpers from the global

// /lib/Helpers.js
Helpers = _.extend(global.Helpers, {/* functions */});  // works in node, but...

Both of which suck.

1)'s syntax is horrible.
2) works in node, but there is no global in browsers. So it fails it's purpose.

So I gave up and went back to overwriting it the first time in lib, and looking for runtime errors if anything was overwritten.

If you have a handy cross-browser syntax for this, do comment :-) var something = something || {} something.blah = foo;

Here's some other JS shorthand tips.

Solution 4:

Session variables are global and can be accessed in different files/functions easily. Session.setPersistent is used to set the variable name persistently across all files. One might restrict from using session variables when their app is too big as they don't get deleted (hence possible memory leaks) and might give error in the console (if undefined or so). Link to the docs : https://docs.meteor.com/api/session.html