Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I modify a template instance from an event handler in Meteor without Session?

I am trying to implement a hierarchy of expandable post comments like in e.g. Quora, so that the user can click a comment and see any replies.

To this end, I would like to keep track of whether each "comment" template instance is "expanded" or not, toggling the state in the event handler.

I could do this using a whole stack Session variables (i.e. one for each comment), but this seems clumsy since there are an arbitrary number of comments on any given page.

Below is a snippet of what I'm trying at the moment.

JS:

Template.comment_item.events = {
    'click #comment-content': function( e, instance ) {
        this.expanded = true;  // also tried instance.data.expanded = true
    }
};

Template.comment_item.helpers({
    showChildComments: function(){
      this.expanded;
    }
});

HTML:

<template name="comment_item">
  <li class="comment comment-displayed" id="{{_id}}">
   <div class="comment-body">
      <div id="comment-content">
        <!-- some comment data here -->
      </div>
      {{#if showChildComments}}
      <ul class="comment-children comment-list">
          {{#each child_comments}}
            {{> comment_item}}
          {{/each}}
      </ul>
      {{/if}}
    </div>
  </li>
</template>

Unfortunately, when I step through, it seems that in the showChildComments helper, the template instance can't see the expanded variable. I did notice in the docs it says instance.data is read only in the events map.

Is there any way to modify the template instance within the event map directly?

like image 839
francisbyrne Avatar asked Mar 29 '13 06:03

francisbyrne


2 Answers

You can create properties for a template instance inside of it's created event handler. And you can access the template instance as the second argument of the event map function.

Template.comment_item.created = function() {
  this.showChildren = false;
};

Template.comment_item.events({
  'click .comment-content': function(event, template) {
    template.showChildren = true;
  }
});

HOWEVER:

  1. you cannot access your template instance and its properties from within a template helper.
  2. Your template helper needs to be given a "reactive" data source anyways for it to change on the spot.
  3. Even if you go through the trouble of creating your own reactive data source, your approach to this feature will still create X amount of variables for X amount of comments on the page. That seems like an awful lot of variables to have in memory to implement one feature.

What I would suggest that you do instead is simplify your template to this:

<template name="comment_item">
  <li class="comment comment-displayed" id="{{_id}}">
   <div class="comment-body">
      <div class="comment-content">  <!-- changed this to a class -->
        <!-- some comment data here -->
      </div>
    </div>
  </li>
</template>

And then programmatically add the child comments to the comment-body in your event-handler. Keep in mind the following from the Meteor documentation:

A template that you declare as <template name="foo"> ... </template> can be accessed as the function Template.foo, which returns a string of HTML when called.

Oh, and you pass context to a template function as a JSON object. E.g.:

var context = {_id: comment_id, text: "Comment text!"};
Template.comment_item(context);
like image 55
travellingprog Avatar answered Nov 19 '22 15:11

travellingprog


Update:

Meteor now gives you access to template instances from within a helper, using Template.instance(). In fact, it's a great way to increase the reusability of your template.

The example source below is from this article, authored by David Burles of Percolate Studio.

Template.hello.created = function () {
  // counter starts at 0
  this.state = new ReactiveDict();
  this.state.set('counter', 0);
}; 

Template.hello.helpers({
  counter: function () {
    return Template.instance().state.get('counter');
  }
});

Template.hello.events({
  'click button': function (event, template) {
    // increment the counter when button is clicked
    template.state.set('counter', template.state.get('counter') + 1);
  }
});

The article does a great job of illustrating why you would want this approach.

The advantage of storing information in the template instance is that you can make templates increasingly modular and decreasingly dependent on the place they are used.

like image 7
James M. Lay Avatar answered Nov 19 '22 14:11

James M. Lay