RouteHandler
function getProfile(req, res) {
var graphs = dataSaver.getGraphs(req.user.email)
console.log(graphs)
res.render('profile', {
title: 'EZgraph | User Home',
userGraphs: graphs
})
}
Db code
function getGraphs(username) {
model.findOne({
email: username
},
function(err, user) {
if (err) {
console.log('err')
}
if (!user) {
console.log('no user found!')
} else {
var graphs = user.savedGraphs
console.log(graphs)
return graphs
}
}
)
}
using the above two methods I'm trying to pass data read from the DB to a jade view. The problem is that within the scope of the 2nd method that reads from the db, the object is read fine, the console.log call shows me that. Once I return this object though and return to the scope of the route handler, the variable that should be equal to the object no prints as undefined. How do I fix this?
EDIT
In repsonse to the comments I tried the following, it isn't pretty at all but I run into the same problem.
Handler + helper
function getProfile(req, res) {
var graphs = dataSaver.getGraphs(req.user.email, readSuccess)
console.log(graphs);
res.render('profile', {
title: 'EZgraph | User Home',
userGraphs: graphs
})
}
function readSuccess(data) {
return data
}
db code
function getGraphs(username, callback) {
model.findOne({
email: username
},
function(err, user) {
if (err) {
console.log('err')
}
if (!user) {
console.log('no user found!')
} else {
callback(user.savedGraphs)
}
}
)
}
Related
I have two functions as shown below. It is essentially just getting data from a database.
function processRequest(query){
let dynamoData = getDynamoData(query);
console.log('abc')
}
function getDynamoData(key){
var params = {
TableName: 'test_table',
Key: {
'TWEET_KEY' : {S: String(key)}
}
};
// Call DynamoDB to read the item from the table
ddb.getItem(params, function(err, data) {
if (err) {
console.log("Error");
} else {
console.log("Successfully got data from table")
return data.Item;
}
});
}
Currently when I run the code, within the console it prints the following:
abc
Successfully got data from table
However, I need it to print Successfully got data from table before it prints abc.
I know I have to possibly use async within the function however am really struggling to get the code running in order. Would really appreciate it if anyone could help me get the code running in order. Thank you!
You should move both functions into a separate module (if this wasn't done yet) and make them async ones like this:
async function processRequest(query){
let dynamoData = await getDynamoData(query);
console.log('abc')
}
async function getDynamoData(key){
var params = {
TableName: 'test_table',
Key: {
'TWEET_KEY' : {S: String(key)}
}
};
return new Promise((resolve, reject) => {
// Call DynamoDB to read the item from the table
ddb.getItem(params, function(err, data) {
if (err) {
console.log("Error");
reject(err);
} else {
console.log("Successfully got data from table")
resolve(data.Item);
}
});
});
}
You need to make processRequest asynchronous:
async function processRequest(query){
let dynamoData = await getDynamoData(query);
console.log('abc')
}
function getDynamoData(key){
var params = {
TableName: 'test_table',
Key: {
'TWEET_KEY' : {S: String(key)}
}
};
// Call DynamoDB to read the item from the table
ddb.getItem(params, function(err, data) {
if (err) {
console.log("Error");
} else {
console.log("Successfully got data from table")
return data.Item;
}
});
}
I'm trying to create access levels in Node.js using Session. So for example, if the username is "test" it can view a certain page. If the username is "admin" it cannot view that same page. The username is stored in MySQL table "accounts". The table has the column attribute "username". Been trying to try different things but I can't seem to get it to work.
Here's my JS code:
app.get('/ViewPage', function(request, response) {
var connection = request.app.get('pool');
if (request.session.loggedin) {
var username;
connection.query('SELECT username FROM accounts WHERE username = test', function (error, results, fields) {
if (username == "test") {
// do the command here
} else if (username == "admin"){
response.redirect('/');
}
});
} else {
response.redirect('/');
}
});
Edit:
I updated my code as follows but it keeps looping to the "Admin cannot view this page" else if block.
app.get('/Create_Award', function (request, response) {
var connection = request.app.get('pool');
if (request.session.loggedin) {
connection.query('SELECT username FROM accounts', function (error, results, fields) {
{ account: results[0] };
if (error) {
console.log(error);
}
else {
if (results[0].username === "test") {
connection.query('SELECT accounts.id, accounts.username, awardType.id as awardTypeId, awardType.title FROM accounts JOIN awardType WHERE username = ?', [request.session.username], function(error, results, fields) {
response.render('Create_Award.html', { account: results[0], data: results });
console.log('Test account.');
}); //2nd connection
}
else if (results[0].username === "admin") {
response.redirect('/');
console.log('Admin cannot view this page.');
}
else {
response.redirect('/');
}
}
}); //1st connection
} else {
response.redirect('/');
}
});
app.get("/viewpage", async function(req, res) {
var connection = req.app.get("pool");
if (res.session.loggedin) {
try {
const result = await connection.query(
"SELECT * FROM accounts WHERE username = test"
);
if (result[0].username === "test") {
//something
} else if (result[0].username === "admin") {
//something
}
} catch (error) {
//You can log the error
return res.redirect("/");
}
}
return res.redirect("/");
});
There are some things to note here
1) Usage of async/await. It makes the code more readable, since queries can take some time to retrieve the data. Also encapsulating all of it inside a try/catch block so you can handle the error if there's any.
2) Your query callback wasn't used. You should use the results argument to retrieve the data in a callback-way.
3) While this is my opinion, always try to return a response, otherwise you may get "Routing error: Can't set headers after they are sent"
You are never populating your var username as it will always will be undefined and your code will go to else block
app.get('/ViewPage', function (request, response) {
var connection = request.app.get('pool');
if (request.session.loggedin) {
connection.query('SELECT username FROM accounts WHERE username = test', function (error, results, fields) {
if (error) {
console.log(error);
}
else {
if (results[0].username === "test") {
response.redirect('/myCustom');
}
else if (results[0].username === "admin") {
response.redirect('/');
}
else {
response.redirect('/');
}
}
});
} else {
response.redirect('/');
}
});
Update your code and it will work
Edit- From what I can see understand from your updated code, the only thing incorrect is how you are using res.render. have a look here, it takes a callback function
Try this in your code and let me know if it works
response.render('Create_Award.html', { account: results[0], data: results },(err,html)=>{
res.send(html)
});
I'm using async each to iterate over objects and perform a query to populate their children. Each site has locations which have floors which have areas. My sites and locations populate just fine, however, that is where it stops. Sails outputs that it is looking for floors and areas though but they are never assigned. Any ideas?
gg: function (req, res) {
var userID = req.param('id');
User.findOne({ id: userID }).populate('sites').exec(function afterFind(err, foundUser) {
if (err) return res.json({ status: 'failure', message: 'Server error' });
if (foundUser === undefined) return res.json({ status: 'failure', message: 'User was not found' });
var resultToJson = foundUser.toJSON();
var sites = resultToJson.sites;
async.each(sites, function (site, siteCb) {
sails.log.debug('Finding locations for ' + site.name);
Locations.find({ site: site.id }).exec(function afterFind(err, foundLocations) {
if (err) {
siteCb(err);
} else {
site['locations'] = foundLocations;
async.each(site['locations'], function (location, locCb) {
sails.log.debug('Finding floors for ' + location.name);
Floor.find({ location: location.id }).exec(function afterFind(err, foundFloors) {
if (err) {
locCb(err);
} else {
location['floors'] = foundFloors;
async.each(location['floors'], function (floor, floorCb) {
sails.log.debug('Finding areas for ' + floor.name);
Area.find({ floor: floor.id }).exec(function afterFind(err, foundAreas) {
if (err) {
floorCb(err);
} else {
floor['areas'] = foundAreas;
floorCb();
}
});
}, function (floorError) {
if (floorError) {
locCb(floorError);
}
else {
locCb();
}
});
}
});
}, function (locError) {
if (locError) {
siteCb(locError);
} else {
siteCb();
}
});
}
});
}, function (siteError) {
if (siteError) {
sails.log.debug(siteError);
return res.json({ status: 'failure', message: 'Server error' });
} else {
return res.json({ status: 'success', message: 'Sites for user retrieved', sites: sites });
}
});
});
}
This code should be fixed with use of toJSON() on foundLocations. Anytime when you're overriding a defined attribute with populated one (or something else) it will not work when using a ORM returned object, use toJSON() or something and assign to plain JS object.
Ref: https://stackoverflow.com/a/43500017/1435132
Also, any reason to not use populate with Locations.find?
Sorry if the title is not quite descriptive.
I am using Node and trying to use export.module to have clean code.
app.js
// ...
require('./router')(app);
module.exports = app;
router.js
cloudant = require("./helpers/cloudant")
// ...
module.exports = (app) => {
// ...
app.post("/statsPage", function(req, res) {
// ...
var a = cloudant.listUsers();
console.log("from post ", a) // --> it shows ("undefined")
if(a == false || a == undefined ) {
res.render("error");
} else {
res.render("statsPage", {
results: a
});
}
cloudant.js
exports = module.exports = {}
exports.listUsers = function() {
db.find({selector: {_id:{ "$gt": 0}}}, function(err, body) {
if(err) {
console.log(err);
return false;
} else {
console.log(body.docs) // --> it shows results correctly
return body.docs;
}
});
}
I've made the same way others "export" methods, like "insert", so I'm convinced that this issue is not related neither to my db connection or export "config".
The db.find method is asynchronous, so the data you get from the database is only available in the callback function. If you look carefully at the function you're exporting in cloudant.js, you'll see that there is no return statement returning any data, only in the callback function, that doesn't help anything.
There are many ways to solve this (and many, many posts on SO dealing with it).
Simplest solution for you would be to pass your own callback to your listUsers function:
exports.listUsers = function (callback) {
db.find({ selector: { _id: { "$gt": 0 } } }, function (err, body) {
if (err) {
console.log(err);
callback(err);
} else {
callback(body.docs);
}
});
}
router.js
app.post("/statsPage", function(req, res) {
cloudant.listUsers(function (a) {
console.log("from post ", a);
});
});
I've got a single page which is an account settings page. In it, I allow my users to update their avatar (if they've attached an image), change their email (if it has been changed from the original), and change their name and password.
Right now, I'm using async's waterfall method, but am swapping out async for Q since I prefer the syntax (and api). I'm wondering if this is the way that I should be using Q in replacement of async's waterfall.
I'm doing something like this:
exports.settingsAccountPOST = function(req, res) {
var doesEmailExist = function() {
var deferred = Q.defer();
User.findByEmail({
email: req.body.email
}, function(err, user) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(user);
}
});
return deferred.promise;
};
var updateEmail = function(email) {
var deferred = Q.defer();
User.updateEmail({
userId : req.session.user.id,
email : req.body.email
}, function(err, updated) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(updated);
}
});
return deferred.promise;
};
var updateName = function() {
var deferred = Q.defer();
if (req.body.name) {
User.updateName({
userId: req.session.user.id,
name: req.body.name
}, function(err, updated) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(updated);
}
});
return deferred.promise;
}
};
doesEmailExist().then(function(email) {
if (!email) {
return(updateEmail(email));
}
}).then(function() {
return(updateName())
}).then(function() {
res.redirect('/account')
});
};
Say that there is an error with the email address being used. Is there a way to "pass" it to the final call? Use case: Updated password properly, but email update didn't work, so I want to show a session flash to the user telling them they updated their password properly, but there was an issue with updating their email.
I was looking in the docs and it seems I may need to use:
.fin(function () {
});
Is this correct? If so, what should I be passing into that? Just push to an object the error that occurred within the chain and then loop through all errors and display them to the user? Or just return immediately and display the error?
If you are using Q.defer you are generally doing something wrong.
var findByEmail = Q.nbind(User.findByEmail, User);
var updateEmail = Q.nbind(User.updateEmail, User);
var updateName = Q.nbind(User.updateName, User);
//later on...
exports.settingsAccountPOST = function (req, res) {
findByEmail({
email: req.body.email
})
.then(function (user) {
if (!user) {
return updateEmail({
userId: req.session.user.id,
email: req.body.email
});
}
})
.then(function () {
return updateName({
userId: req.session.user.id,
name: req.body.name
})
})
.then(function () {
res.redirect("/account");
})
.catch(function(e){
//Handle any error
});
};