Accessing template instances from helpers

2,115 views
Skip to first unread message

Sacha Greif

unread,
Nov 17, 2012, 11:48:16 PM11/17/12
to meteo...@googlegroups.com
Here's something I've been wondering about for a while: is there a way to access a template instance (the "this" in rendered, created, etc.) from within a template helper?

I'm asking because I often end up repeating the same code in multiple helpers (things like var post= Posts.findOne(Session.get('selectedPostId')); to access the current post), and it would be great to put that in the template instance instead. 

Iain Shigeoka

unread,
Nov 18, 2012, 3:08:14 PM11/18/12
to meteo...@googlegroups.com, meteo...@googlegroups.com
Couldn't you just decorate your Posts global with the helper?

Posts.current = function() {
  return Posts.findOne(Session.get('selectedPostId'))
}

Then in your code, just do Posts.current()

-iain

On Nov 17, 2012, at 8:48 PM, Sacha Greif <sach...@gmail.com> wrote:

Here's something I've been wondering about for a while: is there a way to access a template instance (the "this" in rendered, created, etc.) from within a template helper?

I'm asking because I often end up repeating the same code in multiple helpers (things like var post= Posts.findOne(Session.get('selectedPostId')); to access the current post), and it would be great to put that in the template instance instead. 

--
You received this message because you are subscribed to the Google Groups "meteor-core" group.
To view this discussion on the web visit https://groups.google.com/d/msg/meteor-core/-/0H-Li5UHJP0J.
To post to this group, send email to meteo...@googlegroups.com.
To unsubscribe from this group, send email to meteor-core...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/meteor-core?hl=en.

caedmon

unread,
Nov 21, 2012, 7:30:40 PM11/21/12
to meteo...@googlegroups.com
I'd also like access to the template instance from a helper, but my use case is somewhat different.  I've got a helper that does non-trivial processing which I'd like to avoid repeating.  And I also need to access that same result from other template functions, like rendered and events.  As far as I can tell there's no provision for storing that result anywhere for reuse.

Absent any access to the template instance, what's the recommended approach?

Iain Shigeoka

unread,
Nov 23, 2012, 2:15:31 PM11/23/12
to meteo...@googlegroups.com
The Session object the designed for storing values you can use in your templates. Just call Session.set() where you do the calculation and Session.get() to pull it out in your templates. Meteor will do its reactive magic and make sure when you call set() any templates that use get() will get re-rendered.  

-iain
--
You received this message because you are subscribed to the Google Groups "meteor-core" group.
To view this discussion on the web visit https://groups.google.com/d/msg/meteor-core/-/T1c8bK4aUmsJ.

caedmon

unread,
Nov 24, 2012, 8:20:36 PM11/24/12
to meteo...@googlegroups.com
That's what I thought too, but whenever I've tried to call Session.set() from a helper I get this error:
Exception from Meteor.flush: TypeError: Cannot call method 'visit' of undefined

Iain Shigeoka

unread,
Nov 25, 2012, 1:06:32 AM11/25/12
to meteo...@googlegroups.com, meteo...@googlegroups.com
I'm suspecting that you are doing something outside the box. How are you using the helper? My first guess from the error is the helper is running outside or before the spark or reactive contexts are set up. 

-iain
To view this discussion on the web visit https://groups.google.com/d/msg/meteor-core/-/c9U_ogK0w1EJ.

caedmon

unread,
Nov 25, 2012, 12:36:22 PM11/25/12
to meteo...@googlegroups.com
I'm not directly calling the helper.  It's called when the template is rendered.  I don't know that I have any control over when that happens.  I'm not sure what the boundaries of the box are so I can't say for certain that I'm staying within them.

I was able to work around this somewhat by moving the calculation and Session.set() to an event handler in the referring page, but obviously that's completely the wrong place for it to be, creating more problems than it solves.

matt debergalis

unread,
Nov 26, 2012, 12:29:24 PM11/26/12
to meteo...@googlegroups.com
Interesting pattern.  Here's another option.  Compute the expensive value in a Meteor.autorun and save the result in Session -- that code will rerun anytime the DB dependencies change.  Then reference the memoized Session variable in the template handler.

To view this discussion on the web visit https://groups.google.com/d/msg/meteor-core/-/Agd1i3uy_cUJ.

Iain Shigeoka

unread,
Nov 26, 2012, 2:44:00 PM11/26/12
to meteo...@googlegroups.com
Yup, I think Matt's approach is cleaner and it's how I'm doing it. In most cases you want to drive your long calculations off database changes (I assume that's a pretty safe assumption) so the pattern is somewhere in your client/something.js file:

// Launch this when Meteor is initialized - only runs on the client
Meteor.startup(function() {
  var Messages = new Meteor.Collection('messages')

  // Auto-subscribe so we automatically react to changes on a collection subscription
  Meteor.autosubscribe(function() {
    // Say the subscription depends on some setting like 'currentChatRoom'
    var currentChatRoom = Session.get('currentChatRoom')
    // Since this is in a reactive context, changes to the session's currentChatRoom will automatically rerun this subscription
    Meteor.subscribe('messages',currentChatRoom,function() {
      // Do something expensive with the current chat room's current list of messages
      // if the collection changes rapidly underneath you, this will keep getting re-run which could be expensive, for an actual chat app
      // you may need to incrementally update the stats rather than calculate fresh stats on each list update... at least the stats only
      // get run once per messages collection change rather than in each place that the stats appear in the page templates.
      var messages = Messages.find({}}
      var messageStats = longMetricsCalculation(messages)
      Session.set('chatRoomStats',messageStats)
  }
}

On the server you'd do something like server/something.js - only runs on the server

Meteor.startup(function() {
  var Messages = new Meteor.Collection('messages')
  Meteor.publish('messages',function(currentChatRoom) {
    return Messages.find({room,currentChatRoom})
  }
}

Then in your template javascript file (only on the client):

Template.<nameOfTemplate>.stats = function() {
  return Session.get('chatRoomStats')
}

And in your template you just use the expensive stat (foo.html)

<h3>Trending</h3>
<ul>
{{each stats.trendingTopics}}
  <li>{{title}}</li>
{{/each}}

Meteor will make sure all the magic runs behind the scenes to link up changes in the session to changes in the subscription, to changes in the HTML. It can get a little rube goldberg in design since you have to be thinking about "ok this session change, triggers this collection change, which triggers this session change, which triggers this template update" but it's really all about managing application state via events and session/collection state.

I hope that explanation helps more than confuses. :)

-iain

caedmon

unread,
Nov 26, 2012, 9:13:00 PM11/26/12
to meteo...@googlegroups.com
Thanks for the help, guys.  I'm still missing something I think.  To explain further what I'm doing, I've got one page which presents an index of objects in a collection.  There's a selected object set in Session, which is used to feed a detail template.  There's also a drill down function that navigates to a different page which uses the same selected object.  It's only when this page is rendered that I want to do the expensive calcs.  So there's no db or Session change to trigger the calculation before the first helper gets called.

I'm sure there must be some way of doing this, I just haven't been able to figure it out yet.

Iain Shigeoka

unread,
Nov 26, 2012, 11:48:15 PM11/26/12
to meteo...@googlegroups.com
On Nov 26, 2012, at 6:13 PM, caedmon <gril...@gmail.com> wrote:

> Thanks for the help, guys. I'm still missing something I think. To explain further what I'm doing, I've got one page which presents an index of objects in a collection. There's a selected object set in Session, which is used to feed a detail template. There's also a drill down function that navigates to a different page which uses the same selected object. It's only when this page is rendered that I want to do the expensive calcs. So there's no db or Session change to trigger the calculation before the first helper gets called.

When you do the page drill down set a session variable (in your event handler) and use that session variable change to trigger the expensive operation. In your expensive calculation function just check if the session variable is set and do the calculation. When the session variable is not calculated then the function basically does nothing. When the session is set then the calculation runs.

-iain

fabian.vo...@tunedin.de

unread,
Sep 6, 2013, 4:39:26 AM9/6/13
to meteo...@googlegroups.com
Hey,
 i know this post i old, but i'm still wondering why Accessing the template instance from within a helper is not possible?!

What i see as advantages are:
  • Adding custom properties to the template instance, which are then valid only for that particular instance
  • passing a manual new Deps.Dependency() object to helpers, so they can be made reactive to other changes, on a instance base (on there is no need for the use of session!)
  • and what not i can do then instance based ... :)

Disadvantages
  • the only guess i have is performance..(?)

What you guys think? i'm definitely +1 for instance accessing in helpers...

James Wilson

unread,
Sep 6, 2013, 5:20:20 AM9/6/13
to meteo...@googlegroups.com
Really helpful for me would be to refer to field values as people fill them out.


James Wilson


--
You received this message because you are subscribed to the Google Groups "meteor-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to meteor-core...@googlegroups.com.

To post to this group, send email to meteo...@googlegroups.com.

Fabian Vogelsteller

unread,
Sep 6, 2013, 7:44:59 AM9/6/13
to meteo...@googlegroups.com
inside an event you have access to the template instance, and therefore to inout fields. just use:

<code>
'keyup input': function(e, template){

   template.find(e.currentTarget)...

}
</code>

but the same should apply for helpers too :(



Fabian Vogelsteller
Web Developer

TunedIn Media GmbH
Mulackstr. 19
10119 Berlin
Mobile: +49 1577 156 80 60
E-Mail: fabian.vo...@tunedin.de
Homepage: www.tunedin.de

Registergericht: Amtsgericht Charlottenburg, Handelsregister HRB 133483B
Geschäftsführer: Sebastian Bartz, Kaspar Jost Klippgen, Justin E Scull

This message may contain confidential and/or privileged information. If you are not the addressee or authorized to receive this on behalf of the addressee, you must not use, copy, disclose or take any action based on this message or any information herein. If you have received this message in error, please advise the sender immediately by reply e-mail and delete this message. Thank you for your cooperation.

You received this message because you are subscribed to a topic in the Google Groups "meteor-core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/meteor-core/MWk_THcvfQg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to meteor-core...@googlegroups.com.

Andrew Mao

unread,
Oct 18, 2013, 1:48:48 PM10/18/13
to meteo...@googlegroups.com
Is this still not possible?

Silly Clown

unread,
Oct 21, 2013, 9:54:15 AM10/21/13
to meteo...@googlegroups.com
it would be more intuitive to have way to access parent template instance in helpers; subtemplates helpers.

Andrew Mao

unread,
Apr 3, 2014, 2:37:39 PM4/3/14
to meteo...@googlegroups.com
This is even more necessary in Blaze because the `rendered` callback is no longer a reasonable way to do work: https://github.com/meteor/meteor/issues/2001
Reply all
Reply to author
Forward
0 new messages