0

I have a template helper that depends on the current time:

Template.bunny.alive = function () { 
  if (this.time_to_die < new Date().getTime())
    return true;
  else
    return false;
};

How do I make meteor re-draw the template when the current time passes time_to_die?

<template name="bunny"> 
{{if alive }}
  Your bunny is alive.
{{/if}}
</template>

Edit>

Possible solution would be to use a Session variable and store the time which gets updated in say 10 second intervals. So

Session.setDefault("current_time", new Date().getTime());

and

Meteor.setInterval(function() { 
  Session.set("current_time", new Date().getTime());
}, 10000);

Then I can just use Session.get("current_time") in my helpers to make them nicely reactive...

Feels kinda kludgey though?

jms301
  • 309
  • 2
  • 7

2 Answers2

2

Use a reactive variable for indicating the life state, and use a timer to change it (only) when it dies.

Template.world.created = function(){
   self = this
   self.isAlive = new ReactiveVar(true)
   setTimeout(function(){
      self.isAlive.set(false)
   }, self.time_to_die-Date.now())
}

Template.bunny.alive = function () { 
   return Template.instance().isAlive.get()
}
Peppe L-G
  • 7,351
  • 2
  • 25
  • 50
  • AH nice having a single timeout is clever. Both these solutions have a lot of extra code if you have lots of similar templates though. Would break if the bunny is to die too far in the future (overflowing the setTimeout). I'll probably go for a session based solution since in my actual case accuracy could be ~1min and it doesn't matter. Think your solution is probably the best generally though. – jms301 Sep 12 '14 at 20:46
  • By the way what's the purpose of 'self = this' ? – jms301 Sep 12 '14 at 20:49
  • "overflowing the setTimeout"? Don't know what you mean, but whatever you're thinking I don't think it's something you need to worry about. Inside the function passed to `setTimeout`, self is used to access the template instance. `this` inside the `created` function points to the template instance, but `this` inside the function passed to `setTimeout` does not, hence referring to it via `self` instead. – Peppe L-G Sep 13 '14 at 06:57
  • @jms301, or do you mean that the function passed to `setTimeout` may be called after the template has been destroyed? That is true, one should use `clearTimeout` in the templates destroyed callback to avoid that. – Peppe L-G Sep 13 '14 at 07:31
  • There is a maximum value you can pass to setTimeout, if you pass a value over that it executes immediately. If our bunnies live for several years they'll show up dead until near the end of their lives. See [here](http://stackoverflow.com/questions/3468607/why-does-settimeout-break-for-large-millisecond-delay-values). Can always just test for this case though. – jms301 Sep 13 '14 at 13:17
1

I think you have to use a timer if you want your template to update reactively, the Session variable though, could be avoided using ReactiveVar, but it gets a little tricky when you want to access template instance-scoped variables from child templates.

client/views/world/world.js

Template.world.created=function(){
  // we will store the current time using a template instance scoped ReactiveVar
  this.now=new ReactiveVar(Date.now());
  // use a timer to update the current time every second
  this.timer=Meteor.setInterval(_.bind(function(){
    this.now.set(Date.now());
  },this),1000);
};

Template.world.destroyed=function(){
  // clean up on destroy
  Meteor.clearInterval(this.timer);
};

Template.world.helpers({
  inFiveSecondsFromNow:function(){
    return Date.now()+5000;
  }
});

client/views/wordl/world.html

<template name="world">
  {{> bunny timeToDie=inFiveSecondsFromNow}}
</template>

client/views/bunny/bunny.js

Template.bunny.helpers({
  alive:function(){
    // the "tricky" part (and it doesn't get better with nesting)
    var world=Template.instance().view.parentView.parentView._templateInstance;
    return this.timeToDie>=world.now.get();
  }
});

client/views/bunny/bunny.html

<template name="bunny">
  {{#if alive}}
    Your bunny is alive.
  {{else}}
    Your bunny is dead !
  {{/if}}
</template>

When rendered, the world template example will display "Your bunny is alive." for 5 seconds then "Your bunny is dead !".

If your app is simple, I think the Session variable + global timer is probably OK, the benefits from this example is that we scope both the reactive var and the timer to a template instance, so in case of a large complex single page-app, the Session is not polluted and the timer only executes when we render the world template.

saimeunt
  • 22,666
  • 2
  • 56
  • 61