HandleBars - How to Iterate 2 Collections - node.js

I know how to iterate a single collection, but how do I iterate multiple collections using Handlebars?
Below is my routing file
EmployeePayslip.find({"employeeName":""+name}, function(err, db_payslip){
if(err) throw err;
SalaryItems.find({"employeeName":""+name}, function(err,db_salaryItems){
if(err) throw err;
res.render('AdminView/pages/examples/payslip-details',{
payslip:db_payslip,
salaryItem:db_salaryItems
});
});
});
});
How do I do something like below,
{{each payslip}}
<label>{{employeeName}}</label>
{{each salaryItem}}
<label>{{item}}</label>
{{/each}}
{{/each}}
If I only use one each, the iteration works, but if I nested it, it does not seem to display the data. How do I display the data from both collection?

Related

How do I rendering views based on multiple queries in Express?

I have a collection with various data that I would like to use when rendering my ejs page. I only seem to be able to render contents from ONE query and not other queries.
Kindly help me figure out how to render the contents from other queries.
Find below some of the contents of my collection:
Find below my rendered ejs page
Pay special attention to the Unidentified in the Transactions from: Unidentified county sentence.
Now... lets take a look at the code that generates this page:
function county (req, res) {
transModel.findOne({ transCounty : /Nairobi/i,
})
.limit(5)
.select({ transCounty:1})
.exec( (err, result)=> {
if (err) throw err;
console.log('>> ' +result);
console.log('We in county function!');
return result;
});
};
app.get('/list', async (req,res)=> {
console.log('## ' +county());
transModel.find({transIndustry: 'Pharmacy'}, (err, docs)=> {
if (!err)
{
res.render('list', {data : docs, countyName: county});
}
else
{
// res.status(status).send(body);
}
})
});
In the console, the code above logs:
## undefined
>> { _id: 609f7ed8fe1fd8b3193b0b77, transCounty: 'Nairobi' }
We in county function!
And now find below the ejs file contents:
<body>
<h1>
Transactions
</h1>
<p> Transactions from: <b> <%= countyName.transCounty %> </b> County: </p>
<table>
<tr>
<th> _id </th>
<th> Transaction Amount </th>
<th> Transaction Industry </th>
</tr>
<% data.forEach(function(entry) {%>
<tr>
<td> <%=entry._id%> </td>
<td> <%=entry.transAmount%> </td>
<td> <%=entry.transIndustry%> </td>
</tr>
<%});%>
</table>
Kindly help me understand where I am going wrong and how I can get the <%= countyName.transCounty %> to display Nairobi in my rendered ejs file
The main difference between docs and county is that docs is the results from a database-query (ok!), while county is a function which you only reference to and never run (problem 1), but if you were to run it still returns nothing (problem 2), but it does run a query, so you have that going for you!
so, not the complete solution, but just to point you in the right direction:
You need to call county at some point using county() with parenthesises.
Since the nature of database queries is that they are asynchronous you need to either use a callback pattern or a promise-based solution.
A callback solution could look like:
// Here we are calling the function, and the entire function-argument is the callback.
county((countyResult /* the callback will be called with result in the future */) => {
// wrap your existing code,
// because the result will only be available inside the callback
transModel.find({transIndustry: 'Pharmacy'}, (err, docs) => {
...
res.render('list', {data : docs, countyName: countyResult});
});
});
And the called function could look something like this:
function county (callback /* accept a callback */) {
transModel.findOne({ transCounty : /Nairobi/i })
.limit(5)
.select({ transCounty:1})
.exec((err, result) => {
if (err) throw err;
callback(result); // Use the callback when you have the result.
});
}
Because the need to wrap callbacks the result never becomes very pretty.
Instead you could use promises. for example:
let countyResult = await county();
let docs = await transModel.find({transIndustry: 'Pharmacy'}).exec();
res.render('list', {data : docs, countyName: countyResult});
And the called function could look something like this:
function county (req, res) {
return transModel
.findOne({ transCounty : /Nairobi/i })
.limit(5)
.select({ transCounty: 1})
.exec(); // If you use exec without a callback it'll return a promise.
};
This is much cleaner but error handling needs to be done using try/catch.
For example:
let countyResult;
try {
countyResult = await county();
catch(err) {
throw err;
// Of course, if all you do is throw, then you don't even need the `try/catch`.
// Try/catch is to catch thrown errors and handle them.
}
You still might want to check that the result contains anything. A query with no data found is not an error.
Disclaimer: The code is for describing the flow. I've tried to get it right, but nothing here is tested, so there is bound to be errors.

Data passed to Handlebars not showing

I want to display items from the database which I already log the result to the console. The console displays the result. but handlebars not displaying data.
This is controller
exports.createCategory = function(req, res, next){
knex('product_category')
.select()
.then(function(errors, result){
res.render('administration/category', { result, errors });
});
}
This is my router
router.get('/category', adminControllers.createCategory);
and this is my handlebar
<div class="col-md-4 col-lg-4">
{{#each result}}
<h1>{{category_name}}</h1>
{{/each}}
</div>
Just do debugging 101.
Add debug prints and check that you get correct data from DB and that it even executes.
Add to template some field to show you error too. Now if error is returned from query, nothing is shown.
Also add some static test attribute that you pass to template and print it to make sure that you are using handlebars template correctly.
I later got it.
Its the knex query.
after studying the knex docs and some online resources, I discovered I can also do this
knex('table').then(function(result)){
res.render('/', {result});
}
but I don't why this didn't work
knex('table').select().then(function(result)){
res.render('/', {result});
}

Nodejs and Handlebars - list duplicating on refresh, instead of refreshing

I've been playing around with Handlebars templating. Current practice is a simple todo list. On first load it's just fine, but if I refresh my page, it doesn't actually refresh. Instead, it duplicates the results of my GET and appends them to the list.
So, first time the list is like:
Get groceries
Take kids to soccer
Then upon refresh I get:
Get groceries
Take kids to soccer
Get groceries
Take kids to soccer
index.js GET method
app.get('/', (req, res) => {
let query = "select * from todos";
db.query(query, function(err, rows) {
if (err) throw err;
rows.forEach(function(todo) {
todos.push(todo.todo);
console.log(todo.todo)
})
res.render('index', {
todos
});
})
});
index.hbs
<h2>Here's some Todos</h2>
<ul id="list">
{{#each todos}}
<li>{{this}}</li>
{{/each}}
</ul>
It seems todos is a global variable, or valid outside of the for loop scope. For each query it adds the existing db items in todos. Before calling the loop make sure the todos is an empty list:
if (err) throw err;
todos = [];
rows.forEach(function(todo) {
todos.push(todo.todo);
console.log(todo.todo)
})

How to return array of query results (Node.js & MongoDB)

I'm trying to create an array of query results so that I can use that array to populate one of my handlebars pages. Each index in the array holds Comments with a unique attribute. I currently have it set up like this:
This kicks the whole thing off:
//num is determined above
Comment.getComments(num, function(err, comments){
if(err){
console.log("ERROR");
}
console.log(comments);
res.render('page-i-want-to-populate-with-comments', {com : comments});
});
The getComments function calls this (in my models/comment.js):
module.exports.getComments = function(num, callback){
var comArr = [];
for(var i = 0; i < num; i++){
var uniqueId = i;
Comment.find({commentId : uniqueId}, function(err, items){
comArr.push(items);
});
}
callback(comArr);
}
Ideally I would then go to page-i-want-to-populate-with-comments.handlebars and:
{{#each com}}
<div class="comment-wrapper>
{{#each this}}
<div class="comment">
<p>{{this.text}}</p> //the text contained in the comment
</div>
{{/each}}
</div>
{{/each}}
My points of failure now are that comArr is empty when sent through callback() which causes an error in the callback function.
I get that this is probably confusing but this is the best I can explain it. I've been stuck on this for a while so any advice would be appreciated.

nodejs handeling a select form element

I have a (probably very stupid) question.
I have a form I submit to my nodeJS server with Express.
This works perfectly with text inputs and radiobuttons, but now I have to add a select.
The server does not give an error but the select is not parsed properly.
my code:
<select id="chooselang">
<option value="nl" name="language">NL</option>
<option value="en" name="language">EN</option>
</select>
and my server looks like this:
app.post('/settings', function(req, res){
// Fill JSON array with new settings
var myData = {
,name : req.body.name
,mail : req.body.email
,language : req.body.language
,location: req.body.location
}
// Write to JSON file
fs.writeFile(configfilepath, JSON.stringify(myData, null, 4), function(err) {
if(err) {
res.send(500);
console.log(err);
} else {
setTimeout(function () {
res.redirect('back');
}, 2000)
}
});
});
Could someone please tell me what I'm doing wrong?
Probably you will need to add a name attribute in your select and use that to capture the values.
Also, only one of the option values will be sent to the server, so there is no point in assigning name to each one of the options, if that was your intention.

Resources