Meteor Template helpers: {{keyname}} showing blank - javascript

comment.html:
<template name="comment">
<img src="{{photo}}"> //blank
{{photo}} //blank
</template>
comments.js:
Template.comment.helpers({
photo: function(){
Meteor.call('getPhoto', function(error, result) {console.log(result);return result})
}
});
server.js:
Meteor.methods({
getPhoto: function () {
return Meteor.user().services.vk.photo;
}
});
Problem:
console.log returns right value, but {{photo}} is empty.
Question: why 'photo' is empty?

[update]
I just realized what's the problem here.
Meteor.call is calling an async function, like an ajax call. So Meteor.call('getPhoto') would return undefined, the result is only retrievable in the callback
Meteor.call('getPhoto',function(err,result){console.log(result)});
With that in mind, you would need to come up with a way that captures that result in the callback. One solution is to use ReactiveVariable:
You would first need to $ meteor add reactive-var
Template.comment.created = function (){
var $this = this;
$this.photo = new ReactiveVar("loading");
Meteor.call('getPhoto', function (err, result) {
if (err) console.log(err);
$this.photo.set(result);
});
}
And now define your helper to get the value;
//this is optional
Template.comment.helpers({
photo: function(){
return Template.instance().photo.get();
}
});
Another solution is to use Session:
//everything here is in the client
Meteor.call('getPhoto', function(error, result){
Session.set('thePhoto', result);
});
// use reactive Session variable in helper
Template.comment.helpers({
photo: function(){
return Session.get('thePhoto');
}
});
The thing about using Session is that you are setting a global variable and if you have many comments and every comment would need to have an unique photo, Session is probably not the best way to do it.
You are calling the function Meteor.call when you are declaring the helpers.
Template.comment.helpers({
photo: Meteor.call('getPhoto', function(error, result) {console.log(result);return result})
});
So what you are doing is equivalnt to:
var a = Meteor.call('getPhoto', function(error, result) {console.log(result);return result})
Template.comment.helpers({
photo: a //a is just a value
});
For .helpers to work properly, you should be assigning a function to photo instead.
Template.comment.helpers({
photo: function(){
var r;
Meteor.call('getPhoto', function(error, result) {r = result});
return r;
}
});
Under the hood, each helper starts a new Tracker.autorun. When its reactive dependencies change, the helper is rerun. Helpers depend on their data context, passed arguments and other reactive data sources accessed during execution. -From Meteor Doc
.helpers is supposed to be invoked as the very reason why you want to use .helpers is to enable reactivity in your view. Thus what's inside .helpers need to be functions.
If you still don't get what I mean, here is simplified example:
var a = function(){ console.log("hey"); return 1}
var b = a();
var c = a;
b(); //this would run into an error saying that b is a not a function
c(); //this would console.log "hey"

Related

get object of returned mongoose find

Hi i'm trying to access the elements returned from a find in mongoose and having some trouble with the asynchronous and callback situation.
Here is the code for better understanding.
function retrieveBudgets(email, callback) {
models.User.find({email: email},{budget:true}, function(err,budgets) {
if (err) {
callback(err, null);
} else {
callback(null, budgets);
}
});
};
retrieveBudgets(user.email, function(err, budgets) {
if (err) {
console.log(err);
}
budgets.forEach(function(budget){
console.log(JSON.stringify(budget, null, 4));
});
});
So this line console.log(JSON.stringify(budget, null, 4)); is working correctly and printing the objects to screen in json format but how do I store each to an array of objects from here? if I try to push to an array at this same line I get an error.
I have seen some questions that are similar but i am not getting any headway with them.
EDIT:____________________________________________________________
I did a little hack to get it working, i moved res.render up, so that rendering the page was done at the same time as the callback but I cant see this being the right solution any thoughts
var user=req.session.user;
res.locals.budgets=[];
function retrieveBudgets(email, callback) {
models.User.find({email: email},{budget:true}, function(err, budgets) {
if (err) {
callback(err, null);
} else {
callback(null, budgets);
}
});
};
retrieveBudgets(user.email, function(err, budgets) {
if (err) {
console.log(err);
}
res.locals.budgets = budgets.map((function(b){ return b; });
res.render('budget/budget.jade',{ csrfToken: req.csrfToken() });
});
This works I can access budgets through locals so any feedback on this would be great I doubt its the right way to do it?
the budgets return value that you get from the retrieveBudgets call is already an array.
this is evidenced by your call to budgets.forEach which is a method on arrays.
is there a specific need to create a new array from the items? that can be easily done:
var myNewArray = budgets.map((function(b){ return b; });
this one line of code will map the original budgets array into a new array containing each of the budget items.
there are other methods of creating a new array, depending on what you need to do exactly
update from comments below
what i really want to do to is use the budgets outside of the query so I can pass it to the view
in that case, you need to render the view from within the callback and pass the budgets to the view:
router.get("/foo", function(req, res, next){
retrieveBudgets(user.email, function(err, budgets) {
if (err) { return next(err); }
res.render('budget/budget.jade',{
budgets: budgets,
csrfToken: req.csrfToken()
});
});
});
This is the only, and correct, way to make this work.
If you tried to do it without waiting for the callback to finish, you would not have any data in your budgets array. Therefore, you must wait for the callback to be executed and then render your view with the budgets (or single budget or whatever) passed to the view.
(There are variations of this using promises, but I find callbacks to be the easier way to handle this.)
Turn the line in which you're passing an object to your jade file
res.render('budget/budget.jade',{
csrfToken: req.csrfToken(),
budgets: budgets.map(function(b) {return b;})
});
This will pass budgets to your jade file, and you should be able to access it there.

[Meteor call]: Loses prototype information

My Object uses the javascript Object Prototypes.
I defined the following:
Game = function(id, userId, tiles){
this._userId = userId;
this._tiles = tiles;
};
Game.prototype = {
get userId() {
return this._userId;
},
get tiles() {
return this._tiles;
}
}
I then create on the server a game through a meteor call and use the return value:
Meteor.methods({
'fetchGame': function(userId){
var future = new Future();
var url = "http://...";
Meteor.http.get(url, function(err, res){
if (err){
future.return({status: "error", error: err});
} else {
var game = new Game(userId, res.data);
future.return({status: "success", data: game});
}
});
return future.wait();
}
});
The strange thing is, I can call inside the method the prototype functions:
console.log(game.userId)
console.log(game.tiles)
But not when the object gets returned:
Meteor.call('fetchGame', userId, function(error, result) {
var game = result.data;
console.log(game.userId)
console.log(game.tiles)
});
game.userId and game.tiles both return undefined, although the object gets correctly returned (is has values of _userId and _tiles).
It is as if the prototype functions have been lost during the return from the future.
What exactly might be the reason for this?
Data passed to or from a method will be serialized via EJSON. The conversion is stripping your instance of its prototype data (functions are not serialized) which leaves you with a simple object. There isn't a way around this, but that's why it's happening.

Node.js: Undefined return value after database query

I got a file newuser.js (node.js environment featuring a mongodb database managed via mongoose) containing the following code:
//newuser.js
//basically creates new user documents in the database and takes a GET parameter and an externally generated random code (see randomcode.js)
[...]
var randomCode = require ('randomcode');
var newTempUser = new tempUser({name: req.body.name, vericode: randomCode.randomveriCode(parameter)
});
newTempUser.save(function (err){
//some output
});
//randomcode.js
//creates a random sequence of characters (=vericode), checks if code already exists in DB and restarts function if so or returns generated code
exports.randomveriCode = function randomveriCode(parameter){
[...]
var TempUser = conn.model('TempUser', TempUserSchema);
TempUser.count({vericode: generatedcode}, function(err, counter){
if (counter=='0'){
return generatedcode;
}else{
randomveriCode(parameter);
}
});
};
Problem is, that newuser.js throws an error as variable vericode is 'undefined' (thus mongoose model validations fails). The error does not occur if I skip the database query and instantly return the generated code (which in fact has got a value as verified by several console.log instructions). It occurs to me that the db query takes to long and empty or null value returned before query is complete? I thought about introducing promises unless you got any other suggestions or hints what may cause this behaviour?
Kind regards
Igor
Since querying the database is a non-blocking operation, you cannot expect the function call to return the value from the database immediately. Try passing in a callback instead:
// newuser.js
var randomCode = require('randomcode');
randomCode.randomveriCode(parameter, function(err, code) {
if (err) throw err; // TODO: handle better
var newTempUser = new tempUser({name: req.body.name, vericode: code});
newTempUser.save(function (err){
//some output
});
});
// randomcode.js
exports.randomveriCode = function randomveriCode(parameter, cb) {
var TempUser = conn.model('TempUser', TempUserSchema);
TempUser.count({vericode: generatedcode}, function(err, counter) {
if (err) return cb(err);
if (counter == '0') {
cb(null, generatedcode);
} else {
randomveriCode(parameter, cb);
}
});
};
your randomveriCode function contains calls to an asynchronous function and therefore, your function really needs to provide a callback argument like this:
exports.randomveriCode = function randomveriCode(parameter, callback){
[...]
var TempUser = conn.model('TempUser', TempUserSchema);
TempUser.count({vericode: generatedcode}, function(err, counter){
if(err) return callback(err);
if (counter=='0'){
return callback(null, generatedcode);
}else{
randomveriCode(parameter, callback);
}
});
};
You'd then call it like so:
var randomCode = require ('randomcode');
randomCode(function(err, vericode){
if(err) throw err;
var newTempUser = new tempUser({name: req.body.name, vericode: vericode});
newTempUser.save(function(err,newUser){
//do something here
});
});
Btw - you could also use a synchronous function to create a GUID. See https://www.npmjs.org/package/node-uuid.

NodeJS | Passing an object as function parameter

I've had a small problem I couldn't overcome this week, I'm trying to pass a JSON object as a parameter in a function but it always tells me that I can't do that, but I don't want to end up sending the 50 values separately from my json object.
Here is the set up on my app, this is working as intended with express :
app.get('/', routes.index);
Here is the routing index from the previous line of code (Note that I'm using jade for rendering and I'm using the next function to pass parameters to it like the name in this one :
exports.index = function(req, res){
getprofile.profileFunc(function(result) {
res.render('index', { name: result });
});
};
Next it calls the function profileFunc from getprofile :
var profileFunc = function(callback) {
var sapi = require('sapi')('rest');
sapi.userprofile('name_here', function(error, profile) {
var result = [profile.data.name];
callback.apply(null, result);
});
};
exports.profileFunc = profileFunc;
Note that I was only able to pass a string result and have it displayed in the jade render, what I want to do is pass the profile object to use it in the render to display name, age, birthday but I can't get it to work, it will either pass a undefined object or not pass.
Thanks for taking time to read this.
I'll suggest the following:
var profileFunc = function(callback) {
var sapi = require('sapi')('rest');
sapi.userprofile('name_here', function(error, profile) {
callback.apply(null, profile);
});
};
exports.profileFunc = profileFunc;
...
getprofile.profileFunc(function(result) {
res.render('index', result);
});
If you put an Object into Jade's template context, then you'll have to reference it using the variable containing that Object. In your templates, you'd access that using name.foo, name.bar, etc.
Actually the problem is with apply, apply takes array as second argument so when you are trying to send object its not working. Just use callback function without apply like this:-
var profileFunc = function(callback) {
var sapi = require('sapi')('rest');
sapi.userprofile('name_here', function(error, profile) {
callback(profile);
});
};
exports.profileFunc = profileFunc;
...
getprofile.profileFunc(function(result) {
res.render('index', result);
});

node.js mongodb - collection.find().toArray(callback) - callback doesn't get called

I am just starting out with mongodb, but I am running into a problem when trying to use .find() on a collection.
I've created a DataAccessObject which opens a specific databate and then lets your perform operations on it. Here is the code:
The constructor:
var DataAccessObject = function(db_name, host, port){
this.db = new Db(db_name, new Server(host, port, {auto_reconnect: true}, {}));
this.db.open(function(){});
}
A getCollection function:
DataAccessObject.prototype.getCollection = function(collection_name, callback) {
this.db.collection(collection_name, function(error, collection) {
if(error) callback(error);
else callback(null, collection);
});
};
A save function:
DataAccessObject.prototype.save = function(collection_name, data, callback){
this.getCollection(collection_name, function(error, collection){
if(error) callback(error);
else{
//in case it's just one article and not an array of articles
if(typeof (data.length) === 'undefined'){
data = [data];
}
//insert to collection
collection.insert(data, function(){
callback(null, data);
});
}
});
}
And what seems to be the problematic one - a findAll function:
DataAccessObject.prototype.findAll = function(collection_name, callback) {
this.getCollection(collection_name, function(error, collection) {
if(error) callback(error)
else {
collection.find().toArray(function(error, results){
if(error) callback(error);
else callback(null, results);
});
}
});
};
Whenever I try to dao.findAll(error, callback), the callback never gets called.
I've narrowed the problem down to the following part of the code:
collection.find().toArray(function(error, result){
//... whatever is in here never gets executed
});
I've looked at how other people do it. In fact, I'm following this tutorial very closely. No one else seems to have this problem with colelction.find().toArray(), and it doesn't come up in my searches.
Thanks,
Xaan.
You are not using the open callback so if you are trying to make the findall request right after creating the dao then it won't be ready.
If your code is like this, it will not work.
var dao = new DataAccessObject("my_dbase", "localhost", 27017);
dao.findAll("my_collection",function() {console.log(arguments);});
I tested it and it doesn't find records, and it also gives no error. I think it should give an error.
But if you change it so that you give a callback to the constructor, then it should work.
var DataAccessObject = function(db_name, host, port, callback){
this.db = new Db(db_name, new Server(host, port, {auto_reconnect: true}, {}));
this.db.open(callback);
}
And make your code like this.
var dao = new DataAccessObject("my_dbase", "localhost", 27017, function() {
dao.findAll("my_collection",function() {console.log(arguments);});
});

Categories

Resources