Global variables in Meteor
Asked Answered
D

5

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.

Dialogism answered 16/12, 2014 at 16:18 Comment(2)
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 issuesUnvalued
possible duplicate of How can I access constants in the lib/constants.js file in Meteor?Embowed
E
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!

Embowed answered 16/12, 2014 at 17:12 Comment(3)
I try to define my global variables in the lib directory but packages are definitely more robustStreeto
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.Malcah
This isn't a Meteor issue. See below for ReferenceError.Manifestative
E
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 = {}
Engvall answered 17/12, 2014 at 2:13 Comment(1)
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?Serried
M
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.

Manifestative answered 5/9, 2015 at 17:37 Comment(3)
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.Embowed
Can you explain why you referred to ES6 and how this is relevant to the question?Kroll
@Kroll 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 :-/Manifestative
M
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

Murray answered 19/5, 2017 at 7:50 Comment(1)
One can delete session variables too using delete Session.keys[sessionName]. Link : #10744203Murray
G
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.

Guanajuato answered 20/6, 2023 at 11:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.