Have Meteor update a string every second - meteor

This is Meteor's default HTML:
<head>
<title>random-test</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
<h1>Hello World!</h1>
{{greeting}}
<input type="button" value="Click" />
</template>
And this is Meteor's default Javascript code:
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to random-test.";
};
Template.hello.events({
'click input' : function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
I changed the Javascript so I could have it update {{greeting}} every second:
if (Meteor.isClient) {
Template.hello.greeting = "hi";
Meteor.setInterval(function() {
Session.set("greeting", "hello");
console.log("Hi");
}, 1000);
Template.hello.events({
'click input' : function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
It is console.logging "hi" every second. However, it is either not updating the value of greeting or Session.get isn't updating its value as it should (according to Meteor docs).

You need to return the Session.get('greeting') in a helper:
Template.hello.greeting = function() {
return Session.get('greeting');
}

Having Template.hello.greeting = "hi"; makes the template not dependent on Session.get('greeting'), so changes to that "session variable" won't cause any re-renderings. Or what do you expect to happen?

Related

Why isn't this template reactive?

Why isn't this reactive? And more importantly how can it be made reactive?
I'd like the data to be saved in Mongo and used in the template. I could use a ReactiveVar or ReactiveDict. Do I need two copies of the data?
Doesn't Suspects.findOne('bruce') return a reactive object already? I tried putting the human answer directly on Bruce, but it didn't trigger an update.
The events fire, log(this) shows bruce's answer was changed, but the template doesn't re-render. What's the good way to do this?
http://meteorpad.com/pad/KoH5Qu7Fg3osMQ79e/Classification
It's Meteor 1.2 with iron:router added:
<head>
<title>test</title>
</head>
<template name="question">
{{#unless isAnswered 'human'}} <!-- :-< I'm not reacting here -->
<div>Sir, are you classified as human?</div>
<button id="no">No, I am a meat popsicle</button>
<button id="smokeYou">Smoke you</button>
{{else}}
<div> Classified as human? <b>{{answers.human}}</b></div>
{{/unless}}
</template>
And the JavaScript:
// Why isn't this reactive?
if (Meteor.isClient) {
Template.question.helpers({
isAnswered: function (question) { // :-< I'm not reactive
var suspect = Template.instance().data;
return (typeof suspect.answers[question] !== 'undefined');
}
});
Template.question.events({
'click #no': function () {
this.answers.human = "No"; // :-< I'm not reactive
console.log(this);
},
'click #smokeYou': function() {
this.answers.human = "Ouch"; // :-< I'm not reactive
console.log(this);
}
});
}
// Collection
Suspects = new Meteor.Collection('suspects');
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
Suspects.upsert('bruce', { quest: 'for some elements', answers: {}});
});
Meteor.publish('suspects', function() {
return Suspects.find({});
});
}
// Iron Router
Router.route('/', {
template: 'question',
waitOn: function() {
return Meteor.subscribe('suspects');
},
data: function() {
return Suspects.findOne('bruce');
}
});
Thanks :-)
The events are not actually updating the reactive data source (the db record). Instead of doing:
Template.question.events({
'click #no': function () {
this.answers.human = "No";
}
});
The event needs to perform a database action, either through a direct update or through a Meteor.call() to a Meteor.method. For example:
'click #no': function(){
Suspects.update('bruce', {'answers': {'human': 'no'}});
}
If you use this pattern, you will also need to set the correct allow and deny rules to permit the update from client code. http://docs.meteor.com/#/full/allow. Methods generally end up being a better pattern for bigger projects.
Also, I'm not sure off the top of my head that Template.instance().data in your helper is going to be reactive. I would use Template.currentData() instead just to be sure. http://docs.meteor.com/#/full/template_currentdata
Very close you just need to use ReactiveVar by the sound of it it pretty much explains what it's :) http://docs.meteor.com/#/full/reactivevar
and here's how to use it
if (Meteor.isClient) {
Template.question.onCreated(function () {
this.human = new ReactiveVar();
});
Template.question.helpers({
isAnswered: function (question) {
return Template.instance().human.get();
}
});
Template.question.events({
'click #no': function (e, t) {
t.human.set('No');
console.log(t.human.get());
},
'click #smokeYou': function(e, t) {
t.human.set('Ouch');
console.log(t.human.get());
}
});
}
UPDATE: if you're using a cursor I usually like to keep it on the template level not on iron router:
if (Meteor.isClient) {
Template.question.helpers({
isAnswered: function (question) {
return Suspects.findOne('bruce');
}
});
Template.question.events({
'click #no': function (e, t) {
Suspects.update({_id: ''}, {$set: {human: 'No'}});
},
'click #smokeYou': function(e, t) {
Suspects.update({_id: ''}, {$set: {human: 'Ouch'}});
}
});
}

How to perform Meteor.call on Session variable change

Here is what I have:
Templates:
<body>
{{> resultSession}}
{{> resultMethod}}
</body>
<template name="resultSession">
<button>Click me</button>
<p>Session.get('length') returned {{returned}}</p>
</template>
<template name="resultMethod">
<p>Meteor.call returned {{returned}}</p>
</template>
Client-side:
Template.resultSession.events({
'click button': function () {
Session.set('length', Math.floor((Math.random() * 20) + 1));
}
});
Template.resultSession.helpers({
returned: function () {
return Session.get('length');
}
});
Template.resultMethod.helpers({
returned: function() {
Meteor.call('returnArray', Session.get('length'), function (err, res) {
return res.length;
});
}
});
Server-side:
Meteor.methods({
'returnArray': function (length) {
var arr = [];
arr[length - 1] = 0;
return arr;
}
});
TL;DR
You can look at code and play with it here http://meteorpad.com/pad/AkBZq4ZFjJuQuzztz/Meteor.call-on-Session-change
As you can see, my method accepts number and returns the array with length equal to number.
The question is how can I make Meteor.call fire each time Session variable changes?
P.S. Values are returned to two different templates on purpose
Your reactive code is working perfectly.
If you put a console.log in the Meteor.call you will see that the correct answer is coming back from the server.
Template.resultMethod.helpers({
returned: function() {
Meteor.call('returnArray', Session.get('length'), function (err, res) {
console.log('it came back ' + res.length);
return res.length;
});
}
});
I have put a Session variable into the return from the server, so now you can see that your reactive code works very simply - no need for complicated autorun stuff.
<template name="resultMethod">
<p>Meteor.call returned {{returned}}</p>
</template>
Then in the resultMethod helper:
Template.resultMethod.helpers({
returned: function() {
Meteor.call('returnArray', Session.get('length'), function (err, res) {
Session.set('fromServer', res.length + '');
});
return Session.get('fromServer');
}
});
Like #saimeunt said, use Tracker.autorun
Templates:
<body>
{{> resultSession}}
{{> resultMethod}}
</body>
<template name="resultSession">
<button>Click me</button>
<p>Session.get('length') returned {{returned}}</p>
</template>
<template name="resultMethod">
<p>Meteor.call returned {{returned}}</p>
</template>
And code:
Template.resultMethod.rendered = function() {
this.autorun(function (){
Meteor.call('returnArray', Session.get('length'), function (err, res) {
Session.set('result', res);
});
});
}
Template.resultSession.helpers({
returned: function () {
return Session.get('length');
}
});
Template.resultMethod.helpers({
returned: function() {
return Session.get('result');
}
});
Autorun inside rendered stops when the template is not rendered
You could simply refactor your code to call the Meteor method on click event ?
Template.resultSession.events({
'click button': function () {
var length = Math.floor((Math.random() * 20) + 1);
Session.set('length', length);
Meteor.call('returnArray', length, function (err, res) {
Session.set('result', res.length);
});
}
});
Template.resultSession.helpers({
returned: function () {
return Session.get('length');
}
});
Template.resultMethod.helpers({
returned: function() {
return Session.get('result');
}
});
You could also use Tracker.autorun to track modifications of your Session variable and rerun arbitrary code.
Tracker.autorun(function(){
var length = Session.get("length");
console.log("length new value =", length);
});

Tracker afterFlush error : Cannot read value of a property from data context in template rendered callback

I'm making a simple Meteor app that can redirect to a page when user click a link.
On 'redirect' template, I try get the value of property 'url' from the template instance. But I only get right value at the first time I click the link. When I press F5 to refresh 'redirect' page, I keep getting this error message:
Exception from Tracker afterFlush function: Cannot read property 'url' of null
TypeError: Cannot read property 'url' of null
at Template.redirect.rendered (http://localhost:3000/client/redirect.js?abbe5acdbab2c487f7aa42f0d68cf612f472683b:2:17)
at null.
This is where debug.js points to: (line 2)
if (allArgumentsOfTypeString)
console.log.apply(console, [Array.prototype.join.call(arguments, " ")]);
else
console.log.apply(console, arguments);
} else if (typeof Function.prototype.bind === "function") {
// IE9
var log = Function.prototype.bind.call(console.log, console);
log.apply(console, arguments);
} else {
// IE8
Function.prototype.call.call(console.log, console, Array.prototype.slice.call(arguments));
}
Can you tell me why I can't read the value of 'url' property from template data context in template rendered callback?
This is my code (for more details, you can visit my repo):
HTML:
<template name="layout">
{{>yield}}
</template>
<template name="home">
<div id="input">
<input type="text" id="url">
<input type="text" id="description">
<button id="add">Add</button>
</div>
<div id="output">
{{#each urls}}
{{>urlItem}}
{{/each}}
</div>
</template>
<template name="redirect">
<h3>Redirecting to new...{{url}}</h3>
</template>
<template name="urlItem">
<p><a href="{{pathFor 'redirect'}}">
<strong>{{url}}: </strong>
</a>{{des}}</p>
</template>
home.js
Template.home.helpers({
urls: function(){
return UrlCollection.find();
}
});
Template.home.events({
'click #add': function() {
var urlItem = {
url: $('#url').val(),
des: $('#description').val()
};
Meteor.call('urlInsert', urlItem);
}
});
redirect.js
Template.redirect.rendered = function() {
if ( this.data.url ) {
console.log('New location: '+ this.data.url);
} else {
console.log('No where');
}
}
Template.redirect.helpers({
url: function() {
return this.url;
}
});
router.js
Router.configure({
layoutTemplate: 'layout'
})
Router.route('/', {
name: 'home',
waitOn: function() {
Meteor.subscribe('getUrl');
}
});
Router.route('/redirect/:_id', {
name: 'redirect',
waitOn: function() {
Meteor.subscribe('getUrl', this.params._id);
},
data: function() {
return UrlCollection.findOne({_id: this.params._id});
}
});
publication.js
Meteor.publish('getUrl', function(_id) {
if ( _id ) {
return UrlCollection.find({_id: _id});
} else {
return UrlCollection.find();
}
});
Add this
Router.route('/redirect/:_id', {
name: 'redirect',
waitOn: function() {
Meteor.subscribe('getUrl', this.params._id);
},
data: function() {
if(this.ready()){
return UrlCollection.findOne({_id: this.params._id});
}else{
console.log("Not yet");
}
}
});
Tell me if works.
With help from my colleague, I can solve the problem.
My problem comes from wrong Meteor.subscribe syntax. In my code, I forget "return" in waitOn function. This will make Meteor do not know when the data is fully loaded.
Here is the right syntax:
waitOn: function() {
return Meteor.subscribe('getUrl', this.params._id);
}

Setting the reactive data source from Route (Iron Router) is breaking reactivity

I have following code in the Router (Iron Router)
Router.map( function () {
this.route('hello',{path:'/hello',
onBeforeAction: function(){
console.log("QBConnected"+this.params.qbconnected);
Session.set("QBCONNECTED",this.params.qbconnected);
}
});
});
Following code in test.html
<template name="hello">
{{#if QBCONNECTED}}
<h1>Hello World if value={{QBCONNECTED}}</h1>
{{else}}
<h1>Hello World else value={{QBCONNECTED}}</h1>
{{/if}}
{{greeting}}
<input type="button" value="Click" />
</template>
Following code in test.js
if (Meteor.isClient) {
//Session.setDefault('QBCONNECTED',false);
Template.hello.greeting = function () {
return "Welcome to test.";
};
Template.hello.events({
'click input': function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
Template.hello.QBCONNECTED=function(){
console.log(Deps.active+"get QBCONNECTED called"+Session.get('QBCONNECTED'));
return Session.get('QBCONNECTED');
};
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
when I try to run this with the URL http://x.x.x.x:3000/hello?qbconnected=true
I see the string "Hello World if value=true" as expected
But when I try the url http://x.x.x.x:3000/hello?qbconnected=false, I see the string
Hello World if value=false. Can anyone please explain why it is entering the if block when the value of QBCONNECTED is false?
When I try to set the value manually from browser console by "Session.set("QBCONNECTED",true)" it prints "Hello World if value=true" as expected but when I try "Session.set("QBCONNECTED",false)" it prints "Hello World else value= " , can any one explain why QBCONNECTED is loosing it's value when set to false ?
This is happening because false from your querystring gets evaluated to the "false" string and not false boolean value as you might have expected.

Passing data from server block to client block and also calling 3rd party package in Meteor

I am not able to pass data from server block to client block . I am also not sure the way I call self created package is right or wrong .
Here is my folder structure :
x/packages/me.js
x/packages/package.js
x/packages/smart.json
x/x.css
x/x.html
x/x.js
Here is all code :
me.js
Meteor.methods({
getTweets: function () {
var key = "my api key";
var response = Meteor.http.call( "POST", "http://localhost:8000/users.json",
{ params: { appkey: key ,uid: "example" }});
return response.content;
}
});
package.js
Package.describe({
summary: "summary etc"
});
Package.on_use(function (api){
api.use(["http"],["server"]);
api.add_files("me.js","server");
});
smart.json
{
"name": "name example",
"description": "desc",
"homepage":"as",
"author" : "xyz",
"version" : "0.1",
"packages": {}
}
x.html
<head>
<title>x</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
<h1>Hello World!</h1>
{{greeting}}
<input type="button" value="Click" />
</template>
x.js
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to y.";
};
Template.hello.events({
'click input' : function () {
// template data, if any, is available in 'this'
Meteor.call("hiren",function (err, data) {
console.log(data);
if(err) throw err;
});
}
});
}
if (Meteor.isServer) {
Meteor.call("getTweets",function (err, data) {
Meteor.methods({
"hiren":function(){
return data;
}
});
});
}
When I click the "click" button , it just shows undefined
One problem is here:
if (Meteor.isServer) {
Meteor.call("getTweets",function (err, data) {
Meteor.methods({
"hiren": function(){
return data;
},
});
});
}
You define your methods inside a callback function of getTweets method. This is probably not what you want to do, usually those are defined right in the server block as you want to have a predictable API. I cannot think of a reason to do such thing as you did, but even if there is one - you didn't call the getTweets function. So I guess it's a mistake.

Resources