I have the below code that make a call to my DB and pulls down info
// GET the number of apples left in stock
app.get('/apples', function (req, res) {
sql.connect(config, function() {
var request = new sql.Request();
request.query("select quantity from table WHERE type = 'apple'", function(err, recordset) {
var arrayLength = recordset.length;
for (var i = 0; i < arrayLength; i++) {
console.log(recordset[i]["quantity"]);
res.render('index', {results: recordset});
};
});
})
})
this works perfectly .. when i browse to that it sends me to the index page and then i can see the values from the DB spit out in my console log
In my index.pug I then have this
h3(align='middle') #{results}
On the index page I just see this [object Object]
First off all you shouldn't call res.render multiple times in the for loop.
app.get('/apples', function (req, res) {
sql.connect(config, function () {
var request = new sql.Request();
request.query("select quantity from table WHERE type = 'apple'", function (err, recordset) {
var arrayLength = recordset.length;
for (var i = 0; i < arrayLength; i++) {
console.log(recordset[i]["quantity"]);
};
res.render('index', { results: recordset });
});
});
});
Then you should use each in your pug file to iterate resultset.
More doc for iteration : https://pugjs.org/language/iteration.html
each val in results
h3(align='middle')=val.quantity
Related
I'm new to NodeJS, and I'm trying to learn it by building a Shopping cart web app. I'm storing the data in an SQLite database but I can't seem to access it. The .all() method returns a Promise object and I can't figure out how to obtain the items in the database instead.
I followed this tutorial: https://stackabuse.com/a-sqlite-tutorial-with-node-js/ to build a data access object and two models: ItemRepository and CartRepository.
This is the get method in my data access object script:
get(sql, params = []) {
return new Promise((resolve, reject) => {
this.db.get(sql, params, (err, result) => {
if (err) {
console.log('Error running sql: ' + sql)
console.log(err)
reject(err)
} else {
resolve(result)
}
})
}
And this is my index.js
router.get('/', function(req, res, next) {
var dao = new AppDAO('./database.sqlite3');
var itemRepo = new ItemRepository(dao);
var cartRepo = new CartRepository(dao);
items = itemRepo.getAll();
console.log(items);
var itemRows = [];
var rowSize = 3;
for (var i = 0; i < items.length; i += rowSize){
itemRows.push(items.slice(i, i+rowSize));
}
res.render('shop/index', { title: 'sHOP', items: itemRows })
});
I'm a little lost as for how to get the table's content and not the Promise object.
I will recommend you to read promises and async/await or bluebird promises. you will need to use then method on promise to get the actual result as below.
router.get('/', function(req, res, next) {
var dao = new AppDAO('./database.sqlite3');
var itemRepo = new ItemRepository(dao);
var cartRepo = new CartRepository(dao);
itemRepo.getAll().then(( items) =>{
console.log(items);
var itemRows = [];
var rowSize = 3;
for (var i = 0; i < items.length; i += rowSize){
itemRows.push(items.slice(i, i+rowSize));
}
res.render('shop/index', { title: 'sHOP', items: itemRows })
})
});
I have an API that i tweaked to make 2 calls to my DB and pull down information .. I can see the results in my console log so i know it is working
The next part is when it renders the view i need to show the results in two places
Here is the code for the API that makes 2 calls to the DB
function apples(req, res, next) {
sql.connect(config, function () {
var request = new sql.Request();
request.query("select price from table WHERE fruit = 'apples'", function(err, recordsetapples) {
var arrayLength = recordsetapples.length;
for (var i = 0; i < arrayLength; i++) {
console.log(recordsetapples[i]["price"]);
};
res.render('index', { resultsapples: recordsetapples });
return next();
});
});
};
function pear(req, res, next) {
sql.connect(config, function () {
var request = new sql.Request();
request.query("select price from table WHERE fruit = 'pear'", function(err, recordsetpear) {
var arrayLength = recordsetpear.length;
for (var i = 0; i < arrayLength; i++) {
console.log(recordsetpear[i]["price"]);
};
res.render('index', { resultspear: recordsetpear });
next();
});
});
};
app.get('/fruit', apples, pear);
So after that runs I can see the price print in console log .. Then i see this error
Cannot read property 'length' of undefined
What i expect to see if the price appear ... To get that info i have this code
tr
th.hidden-phone Fruit
th.hidden-phone Price
tr
each val in resultsapples
td.hidden-phone Apples
td.hidden-phone !{val.price}
tr
each val in resultspear
td.hidden-phone Pears
td.hidden-phone !{val.price}
The problem is your view expects both lists at the same time but you attempt to render the view twice with each list separately, which means in either scenario one list in the view will be undefined.
Even if you were to fix this, this approach won't work anyway because after the first res.render the HTTP response will end and return to the client. Ideally you would want to make one trip to the DB for both resultsets and then render the view e.g.
sql.connect(config, () => {
const request = new sql.Request();
request.query("select price from table WHERE fruit = 'apples' OR fruit = 'pear'", (err, result) => {
res.render('index', {
resultsapples: result.recordsets[0],
resultspear: result.recordsets[1]
});
});
});
As James mentioned your callbacks are async so you're trying to render the view twice. You also need some error handling in your sql functions
function apples(cb) {
sql.connect(config, function () {
var request = new sql.Request();
request.query("select price from table WHERE fruit = 'apples'", function(err, recordsetapples) {
if(err) {
return cb(err);
}
var arrayLength = recordsetapples.length;
for (var i = 0; i < arrayLength; i++) {
console.log(recordsetapples[i]["price"]);
};
cb(false, recordsetapples);
});
});
};
function pear(cb) {
sql.connect(config, function () {
var request = new sql.Request();
request.query("select price from table WHERE fruit = 'pear'", function(err, recordsetpear) {
if(err){
return cb(err)
}
var arrayLength = recordsetpear.length;
for (var i = 0; i < arrayLength; i++) {
console.log(recordsetpear[i]["price"]);
};
cb(false,recordsetpear);
});
});
};
app.get('/fruit', (req,res) => {
apples((appleerr,appleset) => {
if(appleerr){
//render error page
} else {
pear((pearerr, pearset) => {
if(pearerr) {
//render error page
} else {
return res.render('index', {
resultapples: appleset,
resultpears: pearset
});
}
})
}
});
});
Now for the record, I'm not a fan of nesting the callbacks like this so I would actually recommend you look at Promises and/or async/await but I'm not sure on your coding level so I didn't want to throw too many concepts at you at once.
Also whereas James has merged your SQL statements into one (which is probably the right approach for you) I kept them separate not knowing if you were reusing these individual pieces of code elsewhere and as such didn't want to combine them.
If you are interested in the promise implementation it might look as follows:
function apples() {
return new Promise((resolve,reject) => {
sql.connect(config, function () {
var request = new sql.Request();
request.query("select price from table WHERE fruit = 'apples'", function(err, recordsetapples) {
if(err) {
reject(err);
}
var arrayLength = recordsetapples.length;
for (var i = 0; i < arrayLength; i++) {
console.log(recordsetapples[i]["price"]);
};
resolve(recordsetapples);
});
});
};
function pear() {
return new Promise((resolve,reject) => {
sql.connect(config, function () {
var request = new sql.Request();
request.query("select price from table WHERE fruit = 'pear'", function(err, recordsetpear) {
if(err){
reject(err)
}
var arrayLength = recordsetpear.length;
for (var i = 0; i < arrayLength; i++) {
console.log(recordsetpear[i]["price"]);
};
resolve(recordsetpear);
});
});
});
};
app.get('/fruit', (req,res) => {
var applePromise = apples()
var pearsPromise = applePromise.then((appleSet)) {
return pear()
}
Promise.all([applePromise,pearsPromise]).then((([appleSet,pearSet]) => {
res.render('index', {
resultapples: appleSet,
resultpear: pearSet
});
}).catch((err) => {
//render error
})
});
Having a hard time saving and modifying the result of a MongoJS query in NodeJS.
router.post('/getMySubjects', function (req, res) {
var data = [];
if (req.body.type == 'Professor') {
db.subjects.find({ contractorID: req.body.userId }, function (err, subjects) {
data = subjects; // SUBJECTS ARE NOW SAVED TO DATA SUCCESSFULLY
data.forEach(function(subject) {
db.faculties.find({ _id: mongojs.ObjectID(subject.subjectFor_faculty)}, function (err, faculty) {
subject.faculty = faculty; // BUT HERE I WANT TO ADD A FACULTY (object)
// BASED ON THE subjectFor_faculty (id)
// WHICH IS LOCATED IN EVERY (subject)
// ELEMENT IN DATA ARRAY
});
});
res.send(data); // THE DATA HERE IS UNMODIFIED
// SAME AS DATA ON LINE 6
});
}
});
I presume that I don't yet fully understand how the response works (btw the app is made with express framework), because when the data is first saved on line 6, the next step is sending the data, and only THEN the app goes goes into the forEach loop...
You are making async mongo queries. you have to make them work sync for getting right data. here's implementation using promises.
router.post('/getMySubjects', function (req, res) {
var data = [];
if (req.body.type == 'Professor') {
db.subjects.find({ contractorID: req.body.userId }, function (err, subjects) {
data = subjects;
var promises = [];
data.forEach(function(subject) {
var promise = new Promise(function(resolve, reject) {
db.faculties.find({ _id: mongojs.ObjectID(subject.subjectFor_faculty)}, function (err, faculty) {
resolve(faculty);
});
});
promises.push(promise);
});
Promise.all(promises).then(function(values){
for(var i = 0; i< values.length;i++){
data[i].faculty = values[i];
}
res.send(data);
});
});
}
});
I am unable to get proper output when trying to read data from redis and then send it out to user in json format.
This is my code
//API to get slot for a particular date range
app.get('/listcapacity/:ticketid/:fromdate/:todate', function(req, res) {
var id = req.params.ticketid;
var fromdate = req.params.fromdate;
var todate = req.params.todate;
var result = {};
var data_output = [];
var asyncTasks = [];
var currentDate = new Date(fromdate);
var between = [];
var end = new Date(todate);
while (currentDate <= end) {
var tempdate = new Date(currentDate).toISOString();
var dump = tempdate.toString().split("T");
between.push(dump[0]);
currentDate.setDate(currentDate.getDate() + 1);
}
between.forEach(function(entry) {
asyncTasks.push(function(callback) {
client.exists(id+entry, function (err, reply) {
if (reply === 1) {
console.log("Found");
client.get(id+entry, function (err, reply) {
var output = JSON.parse(reply);
data_output = data_output.concat(output);
});
}
callback();
});
});
});
async.parallel(asyncTasks, function(){
// All tasks are done now
result['data'] = data_output;
result['response'] = 1;
result['message'] = 'vacancies list fetched successfully!';
res.json(result);
});
});
I am always getting the output {"data":[],"response":1,"message":"vacancies list fetched successfully!"}
Even though I have checked that the keys are there in redis and it is always being Found by the application too.
For some reason the redis part is getting executed after the data has been sent to user. Why is this happening? I know redis works in async hence used the async.parallel function to take care of this but even then I endup with the same issue that I would have if I wouldnt have used async. What is the reason for this?
The client.get is an async operation and you have to callback in there. Also, you need an else statement and you need a callback in there as well:
//API to get slot for a particular date range
app.get('/listcapacity/:ticketid/:fromdate/:todate', function (req, res) {
var id = req.params.ticketid;
var fromdate = req.params.fromdate;
var todate = req.params.todate;
var result = {};
var data_output = [];
var asyncTasks = [];
var currentDate = new Date(fromdate);
var between = [];
var end = new Date(todate);
while (currentDate <= end) {
var tempdate = new Date(currentDate).toISOString();
var dump = tempdate.toString().split("T");
between.push(dump[0]);
currentDate.setDate(currentDate.getDate() + 1);
}
between.forEach(function (entry) {
asyncTasks.push(function (callback) {
client.exists(id + entry, function (err, reply) {
if (reply === 1) {
console.log("Found");
client.get(id + entry, function (err, reply) {
var output = JSON.parse(reply);
data_output = data_output.concat(output);
return callback();
});
} else {
return callback();
}
});
});
});
async.parallel(asyncTasks, function () {
// All tasks are done now
result['data'] = data_output;
result['response'] = 1;
result['message'] = 'vacancies list fetched successfully!';
res.json(result);
});
});
I have my application populating an array with names. I can log the array values and it is getting the values, but using postman to make a request on localhost:8888/api/messages my response says matches : [] with the empty array. Why is my array empty in the response if I do indeed populate it?
router.get('/messages', function(request, res) {
var names = [];
ctxioClient.accounts(ID).contacts().get({limit:250, sort_by: "count", sort_order: "desc"},
function ( err, response) {
if(err) throw err;
console.log("getting responses...");
var contacts = response.body;
var matches = contacts.matches;
for (var i = 0; i < matches.length; i++){
names.push(matches[i].name);
matches[i].email;
}
res.json({matches : names});
});
});
This is because the response.json() executes before the ctxioclient.get() happens. Call response.json inside .get() instead. Something like this
router.get('/messages', function(request, response) { // <--- router response
var names = [];
ctxioClient.accounts(ID).contacts().get({ limit: 250,sort_by: "count",sort_order: "desc"},function(err, resp) { // <---- using resp
if (err) throw err;
console.log("getting responses...");
var contacts = response.body;
var matches = contacts.matches;
for (var i = 0; i < matches.length; i++) {
names.push(matches[i].name);
matches[i].email;
}
response.json({ matches: names }); // <--- router response
});
});