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);
});
Related
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'}});
}
});
}
I need to know about to increment helper variable count in meteor.
For example :
<head>
<title>hello</title>
</head>
<body>
<h1>Welcome to Meteor!</h1>
{{> hello}}
</body>
<template name="hello">
<button>Click Me</button>
{{#each arr}}
{{counter}} <!-- How to get the foo index here? --> {{name}}
{{/each}}
</template>
Js Code :
if (Meteor.isClient) {
// counter starts at 0
Template.hello.helpers({
counter: function () {
return Session.get('counter');
}
});
Template.hello.helpers({
arr: function () {
console.log(Session.get('arrres'));
return Session.get('arrres');
}
});
Template.hello.events({
'click button': function () {
Session.set('counter', 0);
Meteor.call('arrfun',10, function (error, res) {
if (!error)
{ arrres = res;
Session.set('arrres', arrres);
console.log(res);
}
else{}
} );
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
Meteor.methods({
arrfun:function arrfun(properties, callback)
{
var obj = [];
for(var i = 0 ; i < 10; i++)
{
var obj1 = new Object();
obj1.name = 'abc'+i;
obj.push(obj1);
}
return obj;
}
});
});
}
The above 'arr' contains list of names that present in object.Now can iterate 'arr' it will comes names.
now can we print names as like 1 abc
2 xyz until 'arr' completed.
So how can we print numbers from 1 to 'arr' length before names.
So please suggest me what to do for this.
It's a little hard to interpret your question but I think you want this:
Template.hello.events({
'click button': function(event) {
Session.set('counter', Math.round(Math.random() * 10));
}
});
Template.hello.helpers({
'arr': function() {
var counter = Session.get('counter');
var arr = [];
for(var i = 0; i < counter; i++) {
arr.push(i + 1);
}
return arr;
}
});
And in your hello template:
<ul>
{{#each arr}}
<li>{{this}}</li>
{{/each}}
</ul>
This will generate a random number between 0-10 every time you click the button, and then the template will count up to this random number in sequence.
if (Meteor.isClient) {
Template.hello.events({
'click input': function () {
//create a new customer
Meteor.call('createCustomer', function (error, result) {
console.log("Error: " + error + " Result: " + result); } );
}
});
}
if (Meteor.isServer) {
Meteor.methods({
createCustomer: function () {
try {
balanced.configure('MyBalancedPaymentsTestKey');
var customer = Meteor._wrapAsync(balanced.marketplace.customers.create());
var callCustomer = customer();
var returnThis = console.log(JSON.stringify(callCustomer, false, 4));
return returnThis;
} catch (e) {
console.log(e);
var caughtFault = JSON.stringify(e, false, 4);
}
return caughtFault;
}
});
}
And I just used the default hello world without the greetings line.
<head>
<title>testCase</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
<h1>Hello World!</h1>
<input type="button" value="Click" />
</template>
On the client side the log prints
Error: undefined Result: {}
On the server side the log prints
[TypeError: Object [object Promise] has no method 'apply']
Any idea how I can wait for that promise instead of returning the blank result?
I'm assuming balanced.marketplace.customers.create returns a Promises/A+ promise. This is an object with a method .then(fulfillmentCallback, rejectionCallback) - the fulfillmentCallback is called when the operation succeeds, and the rejectionCallback is called if the operation had an error. Here's how you could use Futures to synchronously get the value out of a promise:
var Future = Npm.require("fibers/future");
function extractFromPromise(promise) {
var fut = new Future();
promise.then(function (result) {
fut["return"](result);
}, function (error) {
fut["throw"](error);
});
return fut.wait();
}
Then you can just call balanced.marketplace.customers.create normally (no _wrapAsync) to get a promise, then call extractFromPromise on that promise to get the actual result value. If there's an error, then extractFromPromise will throw an exception.
By the way, code in if (Meteor.isServer) blocks is still sent to the client (even if the client doesn't run it), so you don't want to put your API key in there. You can put code in the server directory, and then Meteor won't send it to the client at all.
Update this line
var customer = Meteor._wrapAsync(balanced.marketplace.customer.create)();
Another approach is to use Futures. I use this a lot on the server side to wait for results to return back to the client.
Here's a small example of that I use for logins:
Accounts.login(function (req, user) {
var Future = Npm.require("fibers/future");
var fut = new Future();
HTTP.call("POST", globals.server + 'api/meteor-approvals/token',
{
timeout: 10000, followRedirects: true,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
params: {
username: userName,
password: req.password
}},
function (err, result) {
if (err) {
logger.error("Login error: " + err);
fut.throw(err);
}
else {
fut.return("Success");
}
}
);
return fut.wait();
}
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?
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.