简体   繁体   中英

Making a template helper reactive in Meteor

I am building a chat application and on my "new chats" page I have a list of contacts, which you can select one by one by tapping them (upon which I apply a CSS selected class and push the user id into an array called 'newChatters'.

I want to make this array available to a helper method so I can display a reactive list of names, with all users who have been added to the chat.

The template that I want to display the reactive list in:

<template name="newChatDetails">
  <div class="contactHeader">
    <h2 class="newChatHeader">{{newChatters}}</h2>
  </div>
</template>

The click contactItem event triggered whenever a contact is selected:

Template.contactsLayout.events({
 'click #contactItem': function (e) {
   e.preventDefault();
   $(e.target).toggleClass('selected');
   newChatters.push(this.username);
...

The newChatters array is getting updated correctly so up to this point all is working fine. Now I need to make {{newChatters}} update reactively. Here's what I've tried but it's not right and isn't working:

Template.newChatDetails.helpers({
  newChatters: function() {
    return newChatters;
  }
});

How and where do I use Deps.autorun() to make this work? Do I even need it, as I thought that helper methods auto update on invalidation anyway?

1) Define Tracker.Dependency in the same place where you define your object:

var newChatters = [];
var newChattersDep = new Tracker.Dependency();

2) Use depend() before you read from the object:

Template.newChatDetails.newChatters = function() {
  newChattersDep.depend();
  return newChatters;
};

3) Use changed() after you write:

Template.contactsLayout.events({
  'click #contactItem': function(e, t) {
    ...
    newChatters.push(...);
    newChattersDep.changed();
  },
});

You should use the Session object for this.

Template.contactsLayout.events({
 'click #contactItem': function (e) {
   //...
   newChatters.push(this.username);
   Session.set('newChatters', newChatters);
 }
});

and then

Template.newChatDetails.helpers({
  newChatters: function() {
    return Session.get('newChatters');
  }
});

You could use a local Meteor.Collection cursor as a reactive data source:

var NewChatters = new Meteor.Collection("null");

Template:

<template name="newChatDetails">
  <ul>
    {{#each newChatters}}
      <li>{{username}}</li>
    {{/each}}
  </ul>
</template>

Event:

Template.contactsLayout.events({
  'click #contactItem': function (e) {
    NewChatters.insert({username: this.username});
  }
});

Helper:

Template.newChatDetails.helpers({
  newChatters: function() { return NewChatters.find(); }
});

To mimick the behaviour of Session without polluting the Session , use a ReactiveVar :

 Template.contactsLayout.created = function() {
      this.data.newChatters = new ReactiveVar([]);
 }

 Template.contactsLayout.events({
      'click #contactItem': function (event, template) {
            ...
            template.data.newChatters.set(
                template.data.newChatters.get().push(this.username)
            );
           ...

Then, in the inner template, use the parent reactive data source:

 Template.newChatDetails.helpers({
      newChatters: function() {
           return Template.parentData(1).newChatters.get();
      }
 });

for people who is looking for a workaround for this in the year 2015+ (since the post is of 2014).

I'm implementing a posts wizard pw_module where I need to update data reactively depending on the route parameters :

Router.route('/new-post/:pw_module', function(){
    var pwModule = this.params.pw_module;
    this.render('post_new', {
        data: function(){
            switch (true) {
                case (pwModule == 'basic-info'):
                    return {
                        title: 'Basic info'
                    };
                    break;
                case (pwModule == 'itinerary'):
                    return {
                        title: 'Itinerary'
                    };
                    break;
                default:
            }
        }
    });
}, {
    name: 'post.new'
});

Later in the template just do a:

<h1>{{title}}</h1>

Changing routes

The navigation that updates the URL looks like this:

<nav>
    <a href="{{pathFor route='post.new' pw_module='basic-info'}}">Basic info</a>
    <a href="{{pathFor route='post.new' pw_module='itinerary'}}">Itinerary</a>
</nav>

Hope it still helps someone.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM