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);
});
Related
In my controller called MapController I'm doing a function to do a parse of remote json files, and from an if-else structure add some values in an array called "parsewebservice", apparently everything is working fine but console.log ( parsewebservice); is not returning the values that were passed to the array "parsewebservice" in the place where it is returning it empty. But when I put it inside the forEach it returns, but everything cluttered and repeated then is not the right way.
I wanted to know why the values that were passed to the array "parsewebservice" are not going along with the variable after populada and what would be the correct way to do it?
Here is my code below:
/**
* MapController
*
* #description :: Server-side logic for managing Maps
* #help :: See http://sailsjs.org/#!/documentation/concepts/Controllers
*/
module.exports = {
index: function(req, res, next) {
Data.find(function foundData(err, datas) {
if (err) return next(err);
var parsewebservice = [];
datas.forEach(function(data, index) {
var req = require("request");
var url = data.address + "?f=pjson";
req(url, function(err, res, retorno) {
if (err) {
console.log(err);
} else {
var camadas = JSON.parse(retorno);
if (camadas.mapName) {
camadas.layers.forEach(function(campo, i) {
if (campo.subLayerIds != null) {
} else if (campo.subLayerIds == null) {
parsewebservice.push([i, "dynamicMapLayer", campo.name, data.address]);
}
});
} else if (camadas.serviceDataType) {
parsewebservice.push([null, "imageMapLayer", camadas.name, data.address]);
} else if (camadas.type) {
parsewebservice.push([null, "featureLayer", camadas.name, data.address]);
}
}
});
});
console.log(parsewebservice);
});
},
};
My first comment has to be that you should not combine function(req, res) with var req = require('request')... you lose your access to the original req object!
So, you need to run a list of async tasks, and do something when they are all complete. That will never be entirely easy, and no matter what, you will have to get used to the idea that your code does not run from top to bottom as you've written it. Your console.log at the bottom runs before any of the callbacks (functions you pass in) you pass to your external requests.
The right way to do this is to use promises. It looks like you are using this request library, whose returned requests can only accept callbacks, not be returned as promises. You can create your own promise wrapper for them, or use an alternative library (several are recommended on the page).
I don't want to write a whole intro-to-promises right here, so what I will do is give you a less pretty, but maybe more understandable way to run some code at the completion of all your requests.
Data.find(function foundData(err, datas) {
if (err) return next(err);
var parsewebservice = [];
// here we will write some code that we will run once per returned data
var processResponse = function(resp) {
parsewebservice.push(resp);
if(parsewebservice.length >= datas.length) {
// we are done, that was the final request
console.log(parsewebservice);
return res.send({data: parsewebservice)}); // or whatever
}
};
datas.forEach(function(data, index) {
var request = require("request");
var url = data.address + "?f=pjson";
request(url, function(err, res, retorno) {
// do some processing of retorno...
// call our function to handle the result
processResponse(retorno);
});
});
console.log(parsewebservice); // still an empty array here
});
I solved the problem.
the "request" module is asynchronous so we need to wait for it to respond and then send the response to the view.
To do this we created a function called "foo" to contain the foreach and the request, we made a callback of that function and finally we made the response (res.view) within that function, so that the controller response would only be sent after the response of the "foo" function to the callback. So we were able to parse.json the data from the "data" collection using foreach and the "request" module and send the objects to the view.
Many thanks to all who have helped me, my sincere thanks.
This question might sound duplicate of the one here but my scenario is quiet different. I have getSystemIds.js file like this:
var system_ids_list = {};
var getSystemIDs = function(req, callback) {
var client = //creating an object using internally developed node library
client.request('sql_parameter', {'Query.ID' : query_number},
function(err, req, res){
//Do some stuff to calculate necessary values
system_ids_list[key_value] = value_array;
i+= 1;
}
return(req, callback)
}
)
};
module.exports = getSystemIDs;
Now, as shown in the answer in the link above, I am doing this in app.js
appSSL.get('/sysids', function(req, res) {
var sys_ids = system_ids_list(req, function(err, sys_ids) {
res.render('sysids', sys_ids);
})
});
I am not getting any specific error but the web page never loads as if something is stuck in the process or it does not know where to go next. Can someone help me figure out what would be the best way to do this?
Your getSystemIds() function is never calling the callback that was passed to it so the caller of getSystemIds() never gets a result - thus nothing ever happens on the request.
Change it to this:
var system_ids_list = {};
var getSystemIDs = function (req, callback) {
var client = //creating an object using internally developed node library
client.request('sql_parameter', {'Query.ID': query_number}, function (err, req, res) {
//Do some stuff to calculate necessary values
system_ids_list[key_value] = value_array;
i += 1;
// call the callback now to communicate back the async results
callback(null, system_ids_list);
});
};
module.exports = getSystemIDs;
The way you have your code structured, system_ids_list will accumulate more and more values each time getSystemIDs() is called. That seems a bit of an odd way to structure things so I'm pointing that out in case that is not really what you intend.
Also, your getSystemIDs() function does not return anything so you should change this:
appSSL.get('/sysids', function(req, res) {
var sys_ids = system_ids_list(req, function(err, sys_ids) {
res.render('sysids', sys_ids);
});
});
to this to make it less missleading about what is going on:
appSSL.get('/sysids', function(req, res) {
system_ids_list(req, function(err, sys_ids) {
res.render('sysids', sys_ids);
});
});
And, if res.render() is from a system like ExpressJS, then you probably want to be passing an object and naming a template:
res.render('sometemplate.html', {sysids: sys_ids});
If you want system_ids_list to not accumulate values, but to return a fresh value each time, you can define it within your function like this:
var getSystemIDs = function (req, callback) {
var system_ids_list = {};
var client = //creating an object using internally developed node library
client.request('sql_parameter', {'Query.ID': query_number}, function (err, req, res) {
//Do some stuff to calculate necessary values
system_ids_list[key_value] = value_array;
i += 1;
// call the callback now to communicate back the async results
callback(null, system_ids_list);
});
};
module.exports = getSystemIDs;
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"
I'm working with node.js and redis. I've got a redis database with a bunch of keys. Something like this:
user/chris/potion
user/pete/potion
user/chris/race
user/pete/race
user/chris/weapon
user/pete/weapon
I want to do a redis call which retrieves all user stats, puts the stats into a JS object, then passes it to the client for displaying character stats in the browser. Using javascript I inject the username chris at u into the redis call like this:
KEYS user/u/*
which returns:
1) "user/chris/weapon"
2) "user/chris/race"
3) "user/chris/potion"
Now I can iterate through those results, get the value of each key with GET, and make a javascript object. Seems super simple so I write the code. I quickly run into problems using forEach:
var redis = require('redis');
var client = redis.createClient();
exports.getUserObject = function(requesteduser, callback) {
var userstats = {}; // the object to hold the user stats once retrieved from the db
client.KEYS('user/' + requesteduser + '/*', function(err, replies) {
replies.forEach(function (reply, i) {
client.GET(reply, function(err, value) {
// get the key name so we can populate a js object
var n = reply.lastIndexOf('/');
var key = reply.substring(n + 1, reply.length);
userstats[key] = value;
console.dir(userstats);
callback(null, userstats); // parent expects (err, userstats)
});
});
});
}
When ran, output is like this:
{ weapon: 'twosword' }
{ weapon: 'twosword', race: 'elf' }
{ weapon: 'twosword', race: 'elf', potion: 'mana' }
callback(null, userstats) is called three times. Calling callback more than once will be problematic, since eventually callback will trigger data being sent data to the client.
I think I know what is happening. client.GET is ran three times because I asked it to. The replies array has three elements, so each time the result comes in for client.GET, the callback is called.
What I need to happen is for the forEach loop to finish iterating through all the redis keys, and once that's done, call the callback once. I tried solving this problem first with promises, then with async, because async has async.each. I got stuck solving the problem with both. I'm back to square one now, I'm convinced I have to do something different with forEach to make progress.
How can I accomplish this?
Since you're iterating over replies, you can check when you've reached the last element and only call callback in that instance.
client.KEYS('user/' + requesteduser + '/*', function(err, replies) {
replies.forEach(function (reply, i) {
client.GET(reply, function(err, value) {
// get the key name so we can populate a js object
var n = reply.lastIndexOf('/');
var key = reply.substring(n + 1, reply.length);
userstats[key] = value;
console.dir(userstats);
if (i == replies.length-1) callback(null, userstats); // parent expects (err, userstats)
});
});
});
I know it's a little late to help #Grimtech but I'll leave my opinion for other people that arrives here eventually:
First of all I rather to model #Grimtech's problem differently. Something like this:
client.hmset("user:chris", "weapon", "some weapon", "race", "some race", "potion", "some potion");
client.hmset("user:pete", "weapon", "same weapon", "race", "other race", "potion", "other potion");
Then I would have a method in Node returning a json like the following, assuming that I can have more than one key starting with "user:chris":
router.get('/users/:user_id', function(req, res, next) {
var response = [];
var client = redis.createClient();
client.keys("user:" + req.params.user_id + "*", function (err, users) {
users.forEach(function (key, pos) {
client.hgetall(key, function (err, user) {
response.push(user);
if (pos === users.length - 1) {
res.json(response);
client.quit();
}
});
});
});
});
The if (pos === users.length - 1) solves the async issue.
The json returned will have all the "user:chris" attributes so yo can do whatever you want in the client browser.
I am working on a Node/Express project that has a MongoDB, but I am having a problem passing the result of the Mongo query callback to the route in my app.js file.
Within my MongoDB set-up files, I have the following method which accesses two collections and returns an array of riders:
RiderClass.prototype.findByCarpoolDriver = function(driverID, callback) {
this.getCollection(function(error, rider_collection) { //returns db.riders
if( error ) callback(error)
else {
carpoolClass.findRidesForDriver(driverID, function(err, carpools){
var rider_ids = [];
for(var ii=0;ii<carpools.length;ii++){
rider_ids.push(new ObjectID(carpools[ii].rider_id));
}
rider_collection.find( {_id:{$in : rider_ids}}).toArray(function(e, riders){
console.log(riders); //prints out fine
callback(riders);
});
});
}
});
};
The problem comes in my app.js file, when I call a route:
var RiderClass = require('./rider_class').RiderClass;
var riderClass = new RiderClass('localhost', 27017);
app.get('/driver/:id/rides', function(req, res) {
riderClass.findByCarpoolDriver(req.params.id, function(error, riders) {
console.log(riders); //undefined
res.send(riders);
});
});
The console.log(riders) in my Mongo File prints the array as I'd expect it, but the console.log(riders) in my app.js file returns an undefined. And I don't think it is an async problem because the mongo log is printed before the undefined app.js one.
The only thing I can think is that maybe it a problem with querying multiple collections? But even then I can see from logging it that the array passed to the callback() is good so why isn't it defined in the app.js? Any insight would be greatly appreciated.
When you call riderClass.findByCarpoolDriver, the callback function you're passing in takes two parameters:
function(error, riders) {
console.log(riders); //undefined
res.send(riders);
}
but in both places where you call callback, you're only passing in one parameter. Since you don't use the error parameter, you probably want to change that callback function to just:
function(riders) {
console.log(riders); //undefined
res.send(riders);
}
But then you will want to delete/change this line:
if( error ) callback(error)
since the callback was never using the error to begin with.
Check this:
// one argument passed to callback...
callback(riders);
// ...but two arguments expected:
riderClass.findByCarpoolDriver(req.params.id, function(error, riders) {
...
});
So call the callback with two arguments as well:
callback(null, riders);