Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is best practice for app/user settings in Meteor.js?

I've looked around quite a bit for Meteor examples showing how to implement app settings and user specific settings. The only thing I found was Telesc.pe. And it uses a Settings collection. However, it only has a global (meaning same for everyone) app settings.

Building on that example, I've created my own settings collections which is available on both the server and the client.

// Server and Client
Settings = new Meteor.Collection('settings');

In each Settings record there is a userId field which is equal to 'Default' or the user's id.

{
  ...
  userId: 'Default' // <-- 'Default' or Meteor.userId()
}

I have my publish function publishing both the Default (app) settings AND the user's settings. (Side note: in this app everyone IS logged in, no guests allowed)

// Server
Meteor.publish('settings', function() {
  return Settings.find({userId: {$in: [this.userId, 'default']}});
});

The idea here is that the user will use the Default settings until they change a settings thereby reducing the number of records in the collection.

I've also tried to abstract away a lot of the tedious stuff and create a few helpers to get and set settings for the user.

// Server and Client

// get user specific settings, fallback to default settings
// (not sure if this is the best way, but it works)
settings = function() {
  return Settings.findOne({userId:Meteor.userId()}) 
      || Settings.findOne({userId:'default'});
};

// Get value of a specific setting
getSetting = function(key) {
  return Session.get('settingsLoaded') ? settings()[key] : undefined;
};

// Set value of a specific setting
setSetting = function(key, value) {
   // bunch of logic here to see if a user specific record exists
   // if so, do update
   // if not, do insert
};

So far, this implementation seems to be working fairly well. I can set and get settings in the console via m helper functions.

// in the console...
setSetting('foo', 'bar');
getSetting('foo') // == 'bar'

The issue I'm having happens when I go to start making my app act differently based on certain settings. For example, I have a template called 'phrases' with a variable inside called 'phrases'. I want to change how they are sorted based on the user's settings.

Template.phrases.phrases = function () {
  var sort = {};

  var sortKey = getSetting('sortPhrasesBy'); // if I console.log this it is ALWAYS equal to 'title', 'body', or 'date', which is what I want.
  sort[sortKey] = 1;

  // returns cursor with all phrases, sorted
  return Phrases.find({}, {sort:sort});
};

Except that I keep getting Deps exceptions and I can't tell what is wrong.

Exception from Deps recompute: TypeError: Cannot read property 'nodeName' of null
at Patcher.match (http://localhost:3000/packages/spark.js?3a050592ceb34d6c585c70f1df11e353610be0ab:1540:12)
at http://localhost:3000/packages/spark.js?3a050592ceb34d6c585c70f1df11e353610be0ab:1364:23
at visitNodes (http://localhost:3000/packages/spark.js?3a050592ceb34d6c585c70f1df11e353610be0ab:1320:11)
at visitNodes (http://localhost:3000/packages/spark.js?3a050592ceb34d6c585c70f1df11e353610be0ab:1321:9)
at visitNodes (http://localhost:3000/packages/spark.js?3a050592ceb34d6c585c70f1df11e353610be0ab:1321:9)
at visitNodes (http://localhost:3000/packages/spark.js?3a050592ceb34d6c585c70f1df11e353610be0ab:1321:9)
at visitNodes (http://localhost:3000/packages/spark.js?3a050592ceb34d6c585c70f1df11e353610be0ab:1321:9)
at patch (http://localhost:3000/packages/spark.js?3a050592ceb34d6c585c70f1df11e353610be0ab:1334:3)
at http://localhost:3000/packages/spark.js?3a050592ceb34d6c585c70f1df11e353610be0ab:698:7
at LiveRange.operate (http://localhost:3000/packages/liverange.js?b3097d72d458e645fd4f0021c8ff5189abe8d98a:491:9)

I have no idea what could be causing this. :/

However, this code actually works! It does actually sort the phrases based on what the user has set. But in the console I can see this exception being thrown every time the settings are changed. First load is fine.

So, am I doing something fundamentally wrong here? I must admit I don't yet have my head fully wrapped around what Meteor is doing behind the curtain yet.

I don't know if this is helpful or not. But before I tried implementing a settings collection, I used the Session object. So I had something like this:

// on client
Session.setDefault('sortPhrasesBy', 'title);

Template.phrases.phrases = function () {
  var sort = {};

  var sortKey = Session.get('sortPhrasesBy');
  sort[sortKey] = 1;

  // returns cursor with all phrases, sorted a certain way
  return Phrases.find({}, {sort:sort});
};

This worked without issue. It's just not real flexible.

Is there another way I should be doing this? I'd love to know what the guys building meteor are doing for settings in there personal testing/projects if anyone happens to know.

Sorry so long of a post, I was trying to anticipate what questions might be asked about what I already tried and such.

Thanks for any help you can provide!

like image 660
Johnny Avatar asked Sep 02 '13 17:09

Johnny


People also ask

Which of the following will provide the current user ID if you run it inside a method?

userId() reactive function will give you the ID of the currently logged in user.

What is Meteor JS used for?

Meteor is a full-stack JavaScript platform for developing modern web and mobile applications. Meteor includes a key set of technologies for building connected-client reactive applications, a build tool, and a curated set of packages from the Node. js and general JavaScript community.

Is Meteor JS easy to learn?

Meteor makes developing web applications simple. It's easy to learn, and comes with a pre-built arsenal of powerful functionalities.

Is Meteor a JavaScript framework?

Meteor, or MeteorJS, is a partly proprietary, mostly free and open-source isomorphic JavaScript web framework written using Node.js. Meteor allows for rapid prototyping and produces cross-platform (Android, iOS, Web) code.


1 Answers

The Meteor Accounts system (ex. accounts-password) has the concept of user-specific settings built-in as the 'profile' field of the user object. It is automatically published and is reactive as well (since Meteor.user() is reactive).

Here are the relevant docs: http://docs.meteor.com/#meteor_user

Regarding the "nodeName" error, it is hard to debug but generally when I get that error it is because I am trying to access part of the DOM in a template helper that doesn't actually exist. I've also seen it when I accidentally had two DOM elements with the same ID (fairly easy to do with sub-templates).

To track it down I remove code until it stops happening then add it back piece-by-piece until I find the root cause.

like image 106
alanning Avatar answered Sep 29 '22 03:09

alanning