javascriptmeteormeteor-helper

Meteor.js: Reusing methods and functions in events and helpers


I'm new to Meteor.js and would greatly appreciate any help anyone could give on the following two questions. I'm making a flashcards app, where you click the arrow to show the next flashcard. The flashcards are shuffled beforehand, and you go through the entire deck by clicking the arrow.

Router.route ( '/', function () {
    Session.set ('wordArray', _.shuffle( Words.find().fetch() ) );
    Session.set ( 'index', 0 )
    this.render('wordPage');
})

My wordPage template is as follows:

<template name="wordPage">
    <div class="post">
      <div id = "arrow-right" ></div>
      {{ > wordItem word index }}
    </div>
</template>

My wordPage.js is as follows:

Template.wordPage.helpers ({
    word: function ( index ) {
        return Session.get ( 'wordArray' ) [ index ] ;
    },

    index: function () { return Session.get ( 'index' ); },
})

The wordPage passes on the word and the index to a more detailed tempalte via the method above.

Template.wordPage.events ( { 
    "click #arrow-right": function ( e ) {
        if ( Session.get ('index') < Session.get ('wordArray').length-1 ) {
            console.log(Session.get('index'));
            Session.set ( 'index', Session.get ( 'index' ) + 1);
        }
    }
} )

My two questions:

1) I'd like to shuffle the flashcards every time the page loads, and the only way I can figure out how to do that easily (i.e., without shuffling the entire MongoDB database) is saving the entire flashcards deck on an array via the Sessions variable. How would one implement something where I'm not using the Sessions variable? What's the best way of shuffling the deck every time I go to root, or click a shuffle button somewhere?

2) I'm using Session.get / Session.set a LOT in the wordPage.js file. Is there some way I can save these functions to be accessible in both the wordPage helpers and events? I tried doing something like this:

var word = function ( index ) { return Session.get ( 'wordArray' ) [index]; }

outside of the helpers and events block, and then just trying to use word(index). But it seems like it only works if I'm making word a global variable.

Thanks so much in advance.


Solution

  • When you are having heavy issues about scoping (where do I define that, how do I use it, ok now my code is a mess of Session everywhere I have no idea how I'm manipulating my data and when I try to refactor something I want to burn down my computer) you have a simple solution:
    A package.

    A package would allow you to clearly define your data and import it carefully where you need it. You could define painlessly unique accessors (rather than Session stuff everywhere). You could define once and for all what your data is, how it's accessed, changed, deleted, shuffled, ...


    Here's a boilerplate for your use case.

    meteor create --package cards
    

    Remove the tests. In the package.js, remove the onTest callback, you won't need it for now. You will need underscore and mongo, so add them in the onUse callback:

    api.use('underscore');
    api.use('mongo');
    

    Now in your cards.js fresh file:

    Words = new Meteor.Collection('words'); //Notice the absence of var*
    WordsAccessor = {
      get shuffledWords() {
        return _.shuffle( Words.find().fetch() );
      },
      wordFromIndex : function(index) {
        return Words.find().fetch()[index];
      },
      addWords : function(words) {
        words.forEach(function(word) {
          Words.insert(word);
        });
      }
    };
    

    Finally, export the accessor:

    api.export('WordsAccessor');
    

    With this kind of pattern you can do pretty much whatever you want. You could create an array of words to avoid hitting the minimongo all the time, populate the Words collection on first use, ...


    *No var statement means that the variable is package-scoped and can be exported, then imported either globally with meteor add or in another package scope with api.use.