Making a template helper reactive in Meteor - javascript

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>
Basic info
Itinerary
</nav>
Hope it still helps someone.

Related

ReferenceError: template is not defined - Meteor?

I am making two different app's with Meteor. In first app, witch you can see here, I am using ... template.текст.set( true ); ... and everything is working fine. Now in second app I got error
ReferenceError: template is not defined
So, what is the problem? I Checked, packages are same.
Here is the code of second app:
Template.body.onCreated(function bodyOnCreated() {
this.TrenutniKorisnik = new ReactiveVar(true);
});
Template.PrijavaKorisnika.events({
'submit .Prijava': function(event) {
event.preventDefault();
var korisnik = event.target.КорисничкоИме.value;
var šifra = event.target.Лозинка.value;
if (Korisnici.findOne({КорисничкоИме: korisnik, Шифра: šifra})) { template.TrenutniKorisnik.set( false )};
event.target.КорисничкоИме.value = "";
event.target.Лозинка.value = "";
}
});
Template.body.helpers({
TrenutniKorisnik: function() {
return Template.instance().TrenutniKorisnik.get();
},
});
The template instance is the second parameter in an event handler. Simply change this:
'submit .Prijava': function(event) {
to this:
'submit .Prijava': function(event, template) {
so template will be defined in the function body.
Once you solve that, however you'll find that TrenutniKorisnik isn't defined because it's on the body template and not the current template. One way to solve that is to use a file-scoped variable rather than a template one. Here's an example:
var TrenutniKorisnik = new ReactiveVar(true);
Template.PrijavaKorisnika.events({
'submit .Prijava': function (event) {
...
if (Korisnici.findOne({ КорисничкоИме: korisnik, Шифра: šifra })) {
TrenutniKorisnik.set(false);
}
...
},
});
Template.body.helpers({
TrenutniKorisnik: function () {
return TrenutniKorisnik.get();
},
});

Changing html based on a variable value in Meteor.js

I am trying to display different sets of data based on a variable, without changing the route in meteor.js.
it looks like this:
<template name="somename">
{{#if form_submitted }}
display some data
{{else}}
display other data
{{/if}}
</template>
I am trying to do this with a helper:
Template.somename.helpers({
form_submitted = false;
});
However, when I run this, I get an error.
Also, I would like the variable to change to true when the button is clicked (and then refresh to false after a certain amount of time)
Any ideas?
You'll want to use a combination of template helpers and reactive variables for this:
Template.somename.onCreated( function() {
Template.instance().isFormClicked = new ReactiveVar( false );
});
Template.somename.helpers({
form_submitted: function () {
return Template.instance().isFormClicked.get();
}
});
Template.somename.events({
'click button': function( event, template ) {
template.isFormClicked.set( true );
Meteor.setTimeout( function() {
template.isFormClicked.set( false );
}, 10000); // Reset after 10 seconds.
};
});
Helpers a functions that return a value.
In your specific case it would be:
Template.somename.helpers({
form_submitted: function () {
return true;
}
});
Perhaps you could check the Meteor documentation about helpers http://docs.meteor.com/#/full/template_helpers

Ember.js - I want an Action event (on="") to trigger when there is a transition to a new Route

I want an Action event (on="") to trigger when there is a transition to a new Route.
I've seen the list of Action event handlers and closest I could find is attaching the action to the largest HTML element on the page and triggering it with 'Mousemove". This is a terribly flawed away of going about what I want to do.
So just to draw it out.
<div {{action 'displayEitherHtml1or2'}} class="largestDOMelement">
{{#if showHtml1}}
// html 1 inside
{{/if}}
{{#if showHtml2}}
// html 2 inside
{{/if}}
</div>
'/objects' is a list of objects and clicking one leads to 'object/somenumber'. The action should automatically trigger when I enter the 'object/somenumber' page.
UPDATE: I've taken the contents from the previous update and dumped them into my DocRoute, but nothing it being triggered when I transition to 'doc' through {{#link-to 'doc' this.docID}} {{docTitle}}{{/link-to}}
VpcYeoman.DocRoute = Ember.Route.extend(VpcYeoman.Authenticated,{
toggleLetterSwitch: false,
togglePermitSwitch: false,
activate: function () {
var docTemplateID = this.get('docTemplateID');
if ( docTemplateID == 2) {
this.set('toggleLetterSwitch', true);
this.set('togglePermitSwitch', false);
console.log('docTemplateID equals 2');
} else {
this.set('toggleLetterSwitch', false);
this.set('togglePermitSwitch', true);
}
}
});
UPDATE DOS: setDocID is set in the DocsController to 1. Here's the whole thing.
VpcYeoman.DocsController = Ember.ArrayController.extend({
tempDocId: 1,
actions: {
addDoc: function (params) {
var docTitle = this.get('docTitle');
var docTemplateID = 1;
var docTemplateID = this.get('tempDocId');
console.log(this.get('tempDocId'));
var store = this.store;
var current_object = this;
var doc = current_object.store.createRecord('doc', {
docTitle:docTitle,
docTemplateID:docTemplateID
});
doc.save();
return true;
},
setDocId: function (param) {
this.set('tempDocId', param);
console.log(this.get('tempDocId'))
},
}
});
As #fanta commented, it seems like you're looking for the activate hook within your Route. This gets called when you enter the route where you define it. If you want to call it on every transition, you might consider defining a base route for your application and extending that instead of Em.Route:
App.BaseRoute = Em.Route.extend(
activate: function () {
// Do your thing
}
);
App.YourRoutes = App.BaseRoute.extend()
It's possible that there's a more appropriate place/time to do this, but without knowing quite what your action does, this is probably the best guess.
ETA: Looking at your edit, you won't want all your routes to extend App.BaseRoute the way I did it above; you should probably just include that activate hook explicitly in the routes which need it.

How do I hide dynamic elements in Meteor?

So I have a bunch of templates that will be iterated with {{#each game}} showing the following template:
<template name="game">
{{#if condition}}
<div class="box">
Page 1
</div>
{{else}}
<div class="box">
Page 2
</div>
{{/if}}
</template>
I want to display "Page 2" when the "Page 1" box is clicked, so I have the following:
Template.game.events({
'click .box': function(e) {
Session.set("condition", true);
}
});
But I do not want all of the other game templates to transition to Page 2, just the one that was clicked. How do I accomplish this?
EDIT: The change should only affect the current user, not all users.
Assuming your games are stored in a Meteor.Collection, and condition is a property on the documents that should reflect for all users, not just the current one, you can do something like this:
Template.game.events({
'click .box': function(event, template) {
Games.update(
{_id: template.data._id},
{$set: {condition: !template.data.condition}}
);
}
});
If it should only affect the current user, you can use a template-instance specific session variable and return it with a helper function called condition:
Template.game.events({
'click .box': function(event, template) {
Session.set("condition-" + template.data._id, true);
}
});
Template.game.condition = function() {
return Session.get("condition-" + this._id);
};
You could achieve similar functionality with a local collection.
Don't use Session variables! The very reason is the problem you have, they're equivalent of the old global variables. Use template's data instead, it's local and can be used to control the behavior like you want in this case.
For the template in your example:
Template.game.created = function() {
this.data.conditionValue = 'something';
this.data.conditionDep = new Deps.Dependency();
};
Template.game.condition = function() {
this.conditionDep.depend();
return this.conditionValue;
};
Template.game.events({
'click .box': function(e, t) {
t.data.conditionValue = 'somethingElse';
t.data.conditionDep.changed();
},
});
I also feel using a session with id doesn't sound like the best idea, and found this answer which seems to be better than using session: Using Meteor sessions to toggle templates

How to make reactive UI when variable is changed in Meteor?

I'm not sure that I understand how to keep my web application reactive with Meteor.
I have this very simple template
<body>
{{> simple}}
</body>
<template name="simple">
Counter: {{counter}} <br/>
<button>Increase</button>
</template>
And client side script
var counter = 0;
Template.simple.counter = function () {
return counter;
}
Template.simple.events({
'click button': function () {
counter++;
console.log("counter is ", counter);
Meteor.flush();
}
});
When clicking on button I can see in console that counter variable is increasing properly but nothing happens on UI. Why? I thought it's exactly what Meteor.flush() is intended to do.
The UI isn't reactive all by itself, you need to use one of Meteor's reactive items. In this case, you probably want to use Session. Try the following, instead of the second script you pasted:
Session.set('counter', 0); // Initialize a *reactive* variable
Template.simple.counter = function () {
return Session.get('counter'); // This will automatically update, because Session is reactive
}
Template.simple.events({
'click button': function () {
Session.set('counter', Session.get('counter') + 1);
}
});
You could also build your own reactive data source:
if (Meteor.isClient) {
Sort = {
sort: 1,
dep: new Deps.Dependency,
get: function () {
this.dep.depend();
return this.sort;
},
toggle: function () {
this.sort *= -1;
this.dep.changed();
}
};
Template.leaderboard.players = function () {
return Players.find({}, {sort: {score: Sort.get(), name: 1}});
};
Template.leaderboard.events({
'click input.sort': function () {
Sort.toggle();
}
});
}
Recent versions of Meteor provides the reactive-var package, see here : http://docs.meteor.com/#/full/reactivevar_pkg
To use ReactiveVar, add the reactive-var package to your project by running in your terminal:
meteor add reactive-var

Categories

Resources