28

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.

Jamgreen
  • 10,329
  • 29
  • 113
  • 224
  • is `Schemas` a global variable? do you load it using `require`? maybe you need to show us more code because as the code is written there should be no issues – Logan Murphy Dec 16 '14 at 16:25
  • 3
    possible duplicate of [How can I access constants in the lib/constants.js file in Meteor?](http://stackoverflow.com/questions/26836390/how-can-i-access-constants-in-the-lib-constants-js-file-in-meteor) – Kyll Sep 24 '15 at 11:52

5 Answers5

62

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!

Community
  • 1
  • 1
Kyll
  • 7,036
  • 7
  • 41
  • 64
  • I try to define my global variables in the lib directory but packages are definitely more robust – CleoR Sep 15 '15 at 14:55
  • 3
    I'd like to add that this is really cool because you can have a constants.js file in your /client, and another in your /server directories. And if you want to share a constants file between both, you can create it in /lib/constants. – Adrian Lopez Nov 12 '15 at 09:42
  • This isn't a Meteor issue. See below for ReferenceError. – Michael Cole Jan 31 '17 at 20:50
11

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 = {}
Joshua Soileau
  • 2,933
  • 9
  • 40
  • 51
  • 4
    I'm not sure how I feel about this...is there no ES6/webpack-style way to import variables from another file so that I can avoid globals? – Andy Jun 05 '15 at 14:59
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.

Michael Cole
  • 15,473
  • 7
  • 79
  • 96
  • Note that the third piece of code has to be added at the beginning of every file that needs to use a global variable, for each variable to use. – Kyll Sep 05 '15 at 17:49
  • 1
    Can you explain why you referred to ES6 and how this is relevant to the question? – Fletch Nov 12 '15 at 18:50
  • @Fletch umm... actually no. So I removed it. For some reason, I thought ES6 modules were going to help clean up the global namespace, but I can't find anything about it :-/ – Michael Cole Nov 12 '15 at 21:29
2

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

user3807691
  • 1,284
  • 1
  • 11
  • 29
  • One can delete session variables too using delete Session.keys[sessionName]. Link : https://stackoverflow.com/questions/10743703/how-do-i-delete-or-remove-session-variables – user3807691 Sep 06 '18 at 10:16
0

Typechecking addition:

It looks like using globalThis helps in VS Code TS checking. so instead of

MyCollection = new Mongo.Collection('col')

I write

globalThis.MyCollection = new Mongo.Collection('col')

Eventually I will convert the huge project to ESM, but for now, at least I get rid of all the VS Code reference errors.

Tom Soukup
  • 83
  • 6