Aibynn
Aibynn

Reputation: 115

Meteor Newbie query about best practice

Learning Meteor, which is great, but I'm struggling a bit with "best practice".

For example, I'm building a small example quiz, which looks like:

Question : 1 + 1 = ?    ---> Dropdown box [1, 2, 3]
Question : 1 + 0 = ?    ---> Dropdown box [1, 2, 3]
Question : 1 - 1 = ?    ---> Dropdown box [1, 2, 3]
Question : 2 + 1 = ?    ---> Dropdown box [1, 2, 3]

Then, at the bottom is a Button that says "Score Me".

When that button is pressed, it should go through each question and put a little "Correct" or "Wrong" next to the dropdown box.

All of this looks something like:

<template name="questions">
    <div class="jumbotron">
        {{#each questions}}
            <span class="lead">{{question}} </span>
            <select class="answerDropDown">     
              {{#each answer}}              
                <option value={{this}}>{{this}}</option>
              {{/each}}                 
            </select>

            <span id="correctornot">{{correctornot}}</span>
        {{/each}}       
    </div>
</template>

I'm pulling the data from the DB using:

Questions = new Mongo.Collection("questions");

And everything is working fine. I've plugged in events and it reacts well, etc... etc...

What I can't do easily is see how to make correctornot work. Well... I say that, but what I mean is nothing really makes sense.

I would imagine when I got the data:

Template.questions.helpers({
questions: function() {
  return Questions.find({});
}

I would want to add extra data to each question, but this isn't meant to be saved in the database.

I guess I'm just looking for the correct way to go about it. Even the answers aren't saved anywhere, so when I check to see if it's correct, I'm currently looking at Session variables that are changed on the dropdown select event.

Anyway, all advice welcome.

Cheers.

Upvotes: 0

Views: 67

Answers (1)

SylvainB
SylvainB

Reputation: 4820

What you want is benefit from Blaze's reactivity without persisting information in your database. The answer? Reactive client-side values! They come in different form:

When evaluated in designated contexts (such as helpers or Tracker.autorun), the code included in said context will be ran again every time the value in the reactive variable changes, hence, in the case of helpers, triggering changes in your template.

Example: using Session

Here, we will see the easier way to achieve this, using the built-in Session object. Let's assume your questions template is included in a template called form which contains your submit button:

<template name="form">
  {{> questions}}
  <form id="form">
    <input type="submit" value="Score me" />
  </form>
</template>

First, we must set a submitted session variable to false when the form template gets rendered.

Template.form.onRendered(function () {
  Session.set('submitted', false);
});

Then, catch the form submission event, and instead of submitting, we're going to set our new submitted reactive variable to true.

Template.form.events({
  'submit #form': function (e, t) {
    e.preventDefault();
    // do some prior validation here if needed
    Session.set('submitted', true);
  }
});

Now, all we need to do is to check in a submitted template helper whether submitted is set to true or false. If it is true, we then proceed to display the result of the correctornot helper, which fetches the value selected in the dropdown and compares it to the correct answer for the question stored in the current data context (the each loop). Assuming this answer is stored in a correctAnswer property of your question:

Template.questions.helpers({
  'questions': function() {
    return Questions.find({});
  },
  'submitted': function () {
    return Session.get('submitted');
  },
  'correctornot': function () {
      var userResponse = $('#'+this._id+'.answerDropDown').val();
      return (this.correctAnswer == userResponse);
  }
});

And here is our slightly enhanced questions template:

<template name="questions">
    <div class="jumbotron">
        {{#each questions}}
            <span class="lead">{{question}} </span>
            <select id="{{_id}}" class="answerDropDown">     
              {{#each answer}}              
                <option value={{this}}>{{this}}</option>
              {{/each}}                 
            </select>
            {{#if submitted}}
              <span id="correctornot">{{correctornot}}</span>
            {{/if}}
        {{/each}}       
    </div>
</template>

As you can see, with very little code, you can achieve a nice reactive behavior.

What if I don't want to pollute Session with a pointless flag?

Then, you can look into reactive variables. They're easy to implement once you have the logic in your mind. The little tricky part in your case is that you seem to use nested templates: the submit button triggering the reactive change and the questions list reacting to it are not in the same template. So you will need to navigate between template instances to access the submitted reactive variable. Look at this answer for clues on this subject.

Upvotes: 3

Related Questions