Michael Cole
Michael Cole

Reputation: 16217

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 :-)

Upvotes: 0

Views: 56

Answers (2)

Jeremy S.
Jeremy S.

Reputation: 4639

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

Upvotes: 1

user5084201
user5084201

Reputation:

Very close you just need to use ReactiveVar by the sound of it it pretty much explains what it's :) http://docs.meteor.com/#/full/reactivevar

and here's how to use it

if (Meteor.isClient) {
  Template.question.onCreated(function () {
    this.human = new ReactiveVar();
  });

  Template.question.helpers({
    isAnswered: function (question) {
      return Template.instance().human.get();
    }
  });

  Template.question.events({
    'click #no': function (e, t) {
      t.human.set('No');
      console.log(t.human.get());
    },
    'click #smokeYou': function(e, t) {
      t.human.set('Ouch');
      console.log(t.human.get());
    }
  });
}

UPDATE: if you're using a cursor I usually like to keep it on the template level not on iron router:

if (Meteor.isClient) {
  Template.question.helpers({
    isAnswered: function (question) {
      return Suspects.findOne('bruce');
    }
  });

  Template.question.events({
    'click #no': function (e, t) {
      Suspects.update({_id: ''}, {$set: {human: 'No'}});
    },
    'click #smokeYou': function(e, t) {
      Suspects.update({_id: ''}, {$set: {human: 'Ouch'}});
    }
  });
}

Upvotes: 1

Related Questions