Can't access variable declared inside connection.query function - node.js

I am getting result from query and storing it in a variable which i will send through render but the problem it is giving error variable is not defined. Please help me out. Do correct the code if possible please.
router.get('/create',(req,res)=>{
connection.query('SELECT * FROM purchases',function(error,results,fields){
var voucher = 'PH-'+results.length+1;
connection.query('SELECT * FROM vendors where wid=?',[req.session.w_id],function(err,res,f){
var vendors=res;
connection.query('SELECT * FROM products where wid=?',[req.session.w_id],function(er,r,fi){
var products=r;
})
})
});
res.render('purchase_create',{voucher:voucher,vendors:vendors,products:products});
});
Error is: voucher is not defined

voucher is only available inside the callback function you're passing to connection.query, same thing with the vendors and products. You'll have to move the res.render call into the inner-most function to make this work:
router.get('/create', (req, res) => {
connection.query('SELECT * FROM purchases', function (error, results, fields) {
var voucher = 'PH-' + results.length + 1;
connection.query('SELECT * FROM vendors where wid=?', [req.session.w_id], function (err, res, f) {
var vendors = res;
connection.query('SELECT * FROM products where wid=?', [req.session.w_id], function (er, r, fi) {
var products = r;
res.render('purchase_create', { voucher: voucher, vendors: vendors, products: products });
});
});
});
});

The ‘query’ method of the connection object requires a callback function which will be executed whenever either one of the three events fires – error, fields, results, here denoted by the parameters err, fields and rows respectively.
router.get('/create', (req, res) => {
connection.query('SELECT * FROM purchases', function (error, results, fields)
{
if (error) throw error;
var voucher = 'PH-' + results.length + 1;
connection.query('SELECT * FROM vendors where wid=?', [req.session.w_id], function (err, res, f)
{
if (err) throw err;
var vendors = res;
connection.query('SELECT * FROM products where wid=?', [req.session.w_id], function (er, r, fi)
{
if (er) throw er;
var products = r;
res.render('purchase_create', { voucher: voucher, vendors: vendors, products: products });
});
});
});
});

Related

nodeJS - Cannot set property of undefined

i know this question may be asked about 1000 times here but i cant find the error in my script.
I am trying to iterate through 2 arrays to get a name from an city_id and an organisation_id in a each() loop. i would like to write there values to the "mother"-object to get all informations in one place. Here is the code i've written so far:
let express = require('express');
let router = express.Router();
let request = require('request');
let connection = require('../lib/mysql');
router.get('/', function(req, res) {
if(req.session.loggedin === true){
getList((err, finlist) => {
console.log(finlist)
});
} else {
const cssPath = '/stylesheets/style.css'
res.render('login', { cssPath, title: 'Login' });
}
});
function getList(callback) {
var result = [];
connection.query('SELECT * FROM lists ', (err, rows) => {
if(err) throw err;
var r=0;
rows.forEach(function(item) {
result[r] = item;
getCities((err, cty) => {
result[r].city = cty[item.city_id].name;
getOrganisations((err, org) => {
result[r].organisation = org[item.organisation_id].name;
});
callback(result);
});
r++;
});
});
}
function getCities(callBack) {
var result=[];
connection.query('SELECT * FROM cities ', (err, rows) => {
if (err) throw err;
rows.forEach(function (cty) {
result[cty.id] = cty;
});
if (err) {
callBack(err, null);
} else {
callBack(null, result);
}
});
}
function getOrganisations(callBack) {
var result=[];
connection.query('SELECT * FROM organisations ', (err, rows) => {
if(err) throw err;
rows.forEach(function(org) {
result[org.id] = org;
});
if (err) {
callBack(err, null);
} else {
callBack(null, result);
}
});
};
module.exports = router;
I always get the error
TypeError: Cannot set properties of undefined (setting 'city')
at /opt/alarmprocessor/routes/settings.js:53:32
. . .
which is the line result[r].city = cty[item.city_id].name;
King regards for helping me out :)
Tried to set it as an array, as an Object, made console outputs everywhere... seems all to be fine.. Maybe i am too new to NodeJS so it hasnt been clicked in my Head ;D
This error occur because result[r] doesn't exist line 53. It's declared but doesn't "exist" -> it's undefined. If you perform a mere console.log(result[r]); line 52 you will get an undefined, and you can't set properties (like city) to an undefined value.
The quick fix would be to use optional chaining like this:
result[r]?.city = cty[item.city_id].name;
it won't fix your code, it will only stop crashing by ignoring the assignment.

Express/NodeJS multiple mysql queries for the same route

In a blog made in express/nodejs I am trying to display in the single article page both the article (working fine) and a set of 2 recommended articles. Unfortunately as you can see in the commented bit of the code it doesn't work (can't render the same template twice)
What would be the best approach in this case?
<!-- language: lang-js -->
router.get('/*', function(req, res, next) {
var slug=req.url.replace(/^\//,'').replace(/\/$/,'');
var bg = getRandomInt(5);
if(slug==''){
connection.query('SELECT * FROM `pages` WHERE NOT slug = "about"', function (error, results, fields) {
res.render('index', { title: title, year: year, bg:bg, pages: results });
});
}else{
connection.query('SELECT * FROM `pages` where slug=?', [slug], function (error, results, fields)
{
if(results.length){
res.render('page', { title: results[0].title, page:results[0] });
}else{
console.log(req);
res.render('error',{url: 'http://'+req.headers.host+req.url});
}
});
/* not working apparently you can't send the header of the template twice umhh
connection.query('SELECT * FROM `pages` ORDER by RAND () LIMIT 2', function (error, random, fields)
{
res.render('page', { pages:random});
});
*/
}
});
The way you have it now
Both queries will complete (and call their callback) at unrelated times
res.render will be called multiple times, which does not work because it assumes all of the data is being sent in a single call. So it sends the HTTP headers, which cannot be sent twice.
Updated according to what it looks like you intended. Note this makes the order of the queries sequential, which may not be desirable. You'll want to use the async lib to help manage running them both at the same time and still consolidate the results:
router.get('/*', (req, res, next) => {
const slug = req.url.replace(/^\//, '').replace(/\/$/, '');
const bg = getRandomInt(5);
if (slug == '') {
return connection.query('SELECT * FROM `pages` WHERE NOT slug = "about"', (error, results, fields) => {
res.render('index', { title: title, year: year, bg: bg, pages: results });
});
} else {
return connection.query('SELECT * FROM `pages` where slug=?', [slug], (error, results, fields) => {
if (results.length) {
return connection.query('SELECT * FROM `pages` ORDER by RAND () LIMIT 2', (error, random, fields) => {
if (error) {
// handle error
}
// consolidate renders into a single call
// adjust the template file accordingly
return res.render('page', { title: results[0].title, page: results[0], pages: random });
});
} else {
console.log(req);
return res.render('error', { url: 'http://' + req.headers.host + req.url });
}
});
}
});
Alternatively, consider using bluebird & async/await, this is just another style - to give you options that are new based on node 8+. In this one the queries are kicked off at the same time again.
const bluebird = require('bluebird');
router.get('/*', async (req, res, next) => {
try {
const slug = req.url.replace(/^\//, '').replace(/\/$/, '');
const bg = getRandomInt(5);
if (slug == '') {
const results = await bluebird.fromCallback(cb => connection.query('SELECT * FROM `pages` WHERE NOT slug = "about"', cb));
return res.render('index', { title: title, year: year, bg: bg, pages: results });
} else {
const [results, random] = await Promise.all([
bluebird.fromCallback(cb => connection.query('SELECT * FROM `pages` where slug=?', [slug], cb)),
bluebird.fromCallback(cb => connection.query('SELECT * FROM `pages` ORDER by RAND () LIMIT 2', cb))
]);
if (results && results.length) {
return res.render('page', { title: results[0].title, page: results[0], pages: random });
} else {
return res.render('error', { url: 'http://' + req.headers.host + req.url });
}
}
} catch (e) {
return res.render('error', { url: 'http://' + req.headers.host + req.url });
}
});
You can't render a page twice, otherwise you'll get Error: Can't set headers after they are sent to the client
What you need to do, is fetch the current article and the recommended pages, and render the page once you have the results from both queries.
In order to achieve that I used: Promise.all, and then performed a single res.render
router.get('/*', async (req, res, next) => {
const slug = req.url.replace(/^\//, '').replace(/\/$/, '');
const bg = getRandomInt(5);
if (slug == '') {
const results = await query('SELECT * FROM `pages` WHERE NOT slug = "about"');
return res.render('index', {
title: title,
year: year,
bg: bg,
pages: results
});
}
// Get current article and recommended pages
// Promise.all returns an array where each entry has
// the resolved value of the promise passed at that index
const [article, recommended] = await Promise.all([
query('SELECT * FROM `pages` where slug=?', [slug]),
query('SELECT * FROM `pages` ORDER by RAND () LIMIT 2')
]);
if (article.length) {
// Render the article & recommended pages at once.
res.render('page', {
title: article[0].title,
page: article[0],
pages: recommended
});
} else {
console.log(req);
res.render('error', {
url: 'http://' + req.headers.host + req.url
});
}
});
// Query helper
// You can use promisify...
function query(statement, placeholders = []) {
return new Promise((resolve, reject) => {
connection.query(query, placeholders, (error, results) => {
if(err)
return reject(err);
resolve(results);
});
});
}

Nodejs express async foreach calback to redirect to another route

I have a multer multi-upload form, then i process the images with the Cloud Vision api, do some process on the OCR result and i want to redirect to another route (/next2) after ALL the files are processed.
I edited my code with async.forEach but i got a
TypeError: Cannot read property '0' of undefined
What i got wrong ?
app.post('/vision/upload', upload.array("photos", 10), function(req, res) {
async.forEach(req.files, function (file, cb) {
var post = {url: file.location};
connection.query('SET FOREIGN_KEY_CHECKS=0;', function (err) {
if (err) throw err;
});
connection.query('SELECT * FROM documents WHERE documents.url = ?', file.location, function (err, res54) {
var o2 = isEmpty(res54);
var m9 = {};
if (o2) {
connection.query('INSERT INTO documents SET ?', post, function (err, res5) {
if (err) throw err;
DocumentsNextPage.push(res5.insertId);
});
} else {
connection.query('SELECT * FROM documents WHERE documents.url = ?', file.location, function (err, res9) {
m9 = res9;
});
connection.query('UPDATE documents SET ? WHERE ?', [{url: file.location}, {url: file.location}], function (err) {
if (err) throw err;
DocumentsNextPage.push(m9[0].id);
});
}
if (err) throw err;
});
const req2 = new vision.Request({
image: new vision.Image({
url: file.location
}),
features: [
new vision.Feature('DOCUMENT_TEXT_DETECTION', 10),
]
});
DocumentsNextPage.length = 0;
vision.annotate(req2).then((res2) => {
p1 = JSON.stringify(res2.responses);
p1up = p1.toUpperCase();
x7 = res2.responses[0].textAnnotations;
console.log(x7);
})
occurrencesText = new Occurrences(p1up, {ignored: arrayIgnoredWords});
var tt1 = occurrencesText.getSorted();
var oc1 = toArray(tt1);
var oc2 = unique(oc1);
for (var i = 0; i < 10; i++) {
occurencesResults.push(oc2[i][0]);
var postOccu = {name: oc2[i][0], active: 0, isOccurenceMeta: 1, url: file.location};
connection.query('REPLACE INTO metas SET ?', postOccu, function (err) {
if (err) throw err;
});
}
connection.query(queryString, function (err, rows, fields) {
if (err) throw err;
for (var i in rows) {
var fuse = new Fuse(x7, options);
var result = fuse.search(rows[i].name);
var t1 = isEmpty(result);
if (t1) {
} else {
arrayResults.push(rows[i].name);
var posTag0 = {name: [rows[i].name], active: 0, isOccurenceMeta: 0, url: file.location};
connection.query('INSERT INTO metas SET ?', posTag0, function (err) {
if (err) throw err;
});
}
}
connection.query('SELECT * FROM documents INNER JOIN metas ON documents.url = metas.url WHERE metas.url = ? GROUP BY metas.name ORDER BY documents.url DESC', file.location, function (err, res99) {
if (err) throw err;
for (var i in res99) {
if (res99[i].id != undefined) {
resultMetasDocs[i] = {'documents_id': res99[i].id, 'metas_id': res99[i].id_meta};
}
}
});
for (var i in resultMetasDocs) {
var documentHasMetas = resultMetasDocs[i];
connection.query('REPLACE INTO documents_has_metas SET ?', documentHasMetas, function (err) {
if (err) throw err;
});
}
})
})
cb();
}, function () {
res.redirect('/next2');
});
Several Issues:
var m9 = {};
You have defined m9 to be an object but you later try to access its members as if it was an array. Ensure that m9 has a property you are trying to access and is the of correct type.
connection.query('SELECT * FROM documents WHERE documents.url = ?', file.location, function (err, res9) {
m9 = res9;
});
connection.query('UPDATE documents SET ? WHERE ?', [{url: file.location}, {url: file.location}], function (err) {
if (err) throw err;
DocumentsNextPage.push(m9[0].id);
});`
You are probably trying to access results from one asynchronous operation inside another callback that has no knowledge regarding the state of the asynchronous operation that is supposed to get that result.
connection.query('SELECT * FROM documents WHERE documents.url = ?',
file.location, function (err, res9) {
m9 = res9;
connection.query('UPDATE documents SET ? WHERE ?', [{url:
file.location}, {url: file.location}], function (err) {
if (err) throw err;
/*
because this callback is inside the callback of the former
operation, we can be sure that we will have access to the
results from that operation `m9` in this case
*/
DocumentsNextPage.push(m9[0].id);
});
});

How to render multiple sql query and data in Nodejs

I am having problems rendering multiple data query to page.I have done a lot of research but getting an error like Failed to look up view my code is following:
app.get('/xem', function(req,res){
pool.query("SELECT * FROM phim WHERE slider = '1' ORDER BY id DESC Limit 9", function (error, result, client){
if (!!error){
console.log('Error query');
} else {
res.render('xem', {slider:result});
}
});
pool.query("SELECT * FROM phim WHERE new = '1'", function (error, result, client){
if (!!error){
console.log('Error query');
} else {
res.render('xem', {new:result});
}
});
});
When run it code i give error:
82| <!-- END HEAD -->
83| <h1> ok </h1>
>> 84| <%= new %>
85|
new is not defined
How to fix it?
There are two issues with your approach:
res.render() ends the http request, therefore it will fail when called more than once.
You are executing two asynchronous functions and you don't take care of the order of execution
Try this:
var async = require('async');
app.get('/xem', function(req,res){
var final = {};
async.series({
slider: function(cb) {
pool.query("SELECT * FROM phim WHERE slider = '1' ORDER BY id DESC Limit 9", function (error, result, client){
cb(error, result);
})
},
new: function(cb){
pool.query("SELECT * FROM phim WHERE new = '1'", function (error, result, client){
cb(error, result)
})
}
}, function(error, results) {
if (!error) {
res.render('xem', results);
}
});
});
I don't know if your pool uses promises, so just in case this code uses async approach
The code which you have written is not correct for both queries.
You will get always first query result in response
and in first query result you are sending slider as key and expecting name in response
res.render('xem', {slider:result});
change it with
res.render('xem', {new:result});
Since you are giving name key is in second query result which is not reachable in your case
Thank you everyone. I had it working, sample code:
app.get('/xem', function(req,res){
pool.query("SELECT * FROM phim WHERE slider = '1' ORDER BY id DESC Limit 9", function (error, result, client){
var result1 = result;
link('https://drive.google.com/file/d/0BxG6kVC7OXgrQ1V6bDVsVmJMZFU/view?usp=sharing', function(data){
var dataxem = data;
pool.query("SELECT * FROM user", function (error, result, client){
var user = result;
res.render('xem', {slider:result1, link:data, user:user});
});
});
});
})
app.use('/', (req,res) => {
connection.query('select * from users', function(err, rows, fields){
if(err) return;
console.log(rows[0]);
res.send(rows);
});
});

Node.JS / JavaScript Async Call back issue

I am trying to accomplish the following (should be quite basic and I must be missing something trivial):
Call a function with string that has a select statement
Wait until the DB call completes and the rows (or json equivalent) is returned
Populate http object to return back
Here is code:
util.js
exports.execute = function( query){
if (connection) {
connection.query(query, function(err, rows, fields) {
if (err) throw err;
console.log("from Util - " + JSON.stringify(rows));
return JSON.stringify(rows);
});
}
};
repo.js
var q = "select * from xxx";
var response;
util.execute(q, function (err, r){
if (err){
throw err;
console.log(err);
}
else {
console.log(r);
res.contentType('application/json');
res.write(r);
res.end();
}
});
My problem is while the code within util.js is called and I can see the json in console, it never comes back to the anonymous call back function in the repo.js.
What am I doing wrong here ?
Update
Thanks Ben
I also found the solution in same line ... here is new code:
repo.js:
var send_data = function (req, res, r){
res.contentType('application/json');
res.write(r);
res.end();
}
exports.all = function(req, res){
var q = "select * from XXX";
var response;
util.execute(req, res,q, send_data);
};
util.js:
exports.execute = function(req, res, query, callback){
if (connection) {
connection.query(query, function(err, rows, fields) {
if (err) throw err;
callback(req, res, JSON.stringify(rows)) ;
});
}
};
util.execute only accepts one parameter in your code. It needs to accept a second callback parameter in order to use it the way you do. (Callbacks aren't magic, they're just function calls)
Something like:
exports.execute = function(query,callback){
if (connection) {
connection.query(query, function(err, rows, fields) {
if (err) throw err;
console.log("from Util - " + JSON.stringify(rows));
callback(null,JSON.stringify(rows));
});
}
};
If you'd like it to behave consistently and accept an error parameter, you might want fill that in:
exports.execute = function(query,callback){
if (connection) {
connection.query(query, function(err, rows, fields) {
callback(err,null);
console.log("from Util - " + JSON.stringify(rows));
callback(null,JSON.stringify(rows));
});
}
};

Resources