Search code examples
meteormeteor-blazemeteor-helper

Why isn't this template reactive?


Why isn't this reactive? And more importantly how can it be made reactive?

I'd like the data to be saved in Mongo and used in the template. I could use a ReactiveVar or ReactiveDict. Do I need two copies of the data?

Doesn't Suspects.findOne('bruce') return a reactive object already? I tried putting the human answer directly on Bruce, but it didn't trigger an update.

The events fire, log(this) shows bruce's answer was changed, but the template doesn't re-render. What's the good way to do this?

http://meteorpad.com/pad/KoH5Qu7Fg3osMQ79e/Classification

It's Meteor 1.2 with iron:router added:

<head>
  <title>test</title>
</head>

<template name="question">
  {{#unless isAnswered 'human'}}   <!--  :-<  I'm not reacting here -->
    <div>Sir, are you classified as human?</div>
    <button id="no">No, I am a meat popsicle</button>
    <button id="smokeYou">Smoke you</button>
  {{else}}
    <div> Classified as human?  <b>{{answers.human}}</b></div>
  {{/unless}}
</template>

And the JavaScript:

// Why isn't this reactive?
if (Meteor.isClient) {
  Template.question.helpers({
    isAnswered: function (question) {     // :-<  I'm not reactive
      var suspect = Template.instance().data;
      return (typeof suspect.answers[question] !== 'undefined');
    }
  });

  Template.question.events({
    'click #no': function () {
      this.answers.human = "No"; // :-<  I'm not reactive
      console.log(this);
    },
    'click #smokeYou': function() {
      this.answers.human = "Ouch";    // :-<  I'm not reactive
      console.log(this);
    }
  });
}

// Collection
Suspects = new Meteor.Collection('suspects');
if (Meteor.isServer) {
  Meteor.startup(function () {
    // code to run on server at startup
    Suspects.upsert('bruce', { quest: 'for some elements', answers: {}});
  });
  Meteor.publish('suspects', function() {
    return Suspects.find({});
  });
}

// Iron Router
Router.route('/', {
  template: 'question',
  waitOn: function() {
    return Meteor.subscribe('suspects');
  },
  data: function() {
    return Suspects.findOne('bruce');
  }
});

Thanks :-)


Solution

  • The events are not actually updating the reactive data source (the db record). Instead of doing:

    Template.question.events({
      'click #no': function () {
        this.answers.human = "No";
      }
    });
    

    The event needs to perform a database action, either through a direct update or through a Meteor.call() to a Meteor.method. For example:

    'click #no': function(){
      Suspects.update('bruce', {'answers': {'human': 'no'}});
    }
    

    If you use this pattern, you will also need to set the correct allow and deny rules to permit the update from client code. http://docs.meteor.com/#/full/allow. Methods generally end up being a better pattern for bigger projects.

    Also, I'm not sure off the top of my head that Template.instance().data in your helper is going to be reactive. I would use Template.currentData() instead just to be sure. http://docs.meteor.com/#/full/template_currentdata