accessing template params ejs express - node.js

I'm creating fleet management application, I've set up for vehicles' makes and models and I've established one-to-many relationship in my database model.
I'm trying to present the vehicle make along with its models, however, it doesn't seem to work, below is the code from my routes file:
router.get("/new",function(req,res){
var selectedMake;
Make.find({},function(err,result){
if(err){
console.log("an error occured while fetching the data");
}else{
if(req.query.id){
Make.findById(req.query.id,function(err,foundMake){
console.log("found the query string");
selectedMake = foundMake;
console.log(selectedMake);
})
}
res.render("vehicles/newvehiclemake",{makes:result,selected: selectedMake});
}
})
});
and here is the code where i'm trying to access variable "selected" in my .ejs file
<div class="row">
<div class="col-md-9 col-md-offset-3">
<table class="table table-striped">
<tr>
<th>Available Models</th>
</tr>
<% if(selected) { %>
<% selected.models.forEach(function(model){ %>
<tr><td><%= model.name %></td></tr>
<% }) %>
<% }else { %>
<tr><td>No Models available for the selected Make</td></tr>
<% } %>
</table>
</div>
</div>
the branch where selected should be executed is never reached and always i get No Models available for the selected Make
any clues?

I think that is because your Make.findById method is a asynchronous call. So your callback function(err,foundMake) is called after the res.render
Move your render call into the callback function, then it should work.
if(req.query.id){
Make.findById(req.query.id,function(err,foundMake){
console.log("found the query string");
selectedMake = foundMake;
// after the findById call finished, now it has value.
console.log(selectedMake);
// res.render should be called at this moment.
res.render("vehicles/newvehiclemake",{makes:result,selected: selectedMake});
})
// you would see this line is called before the data is ready.
console.log(selectedMake);
}

Related

Form Select onChange Sends Data/Info to Query on Server

I'm using Nodejs, Express, and EJS.
Here's what works...
I can use an unordered list of hyperlinks and send the info/variable via req.params this way...
db.ejs code
<ul>
<% dbTitle.forEach(function(dbTitle){ %>
<li><%= dbTitle.dbTitle %></li>
<% }) %>
</ul>
server.js code
app.get('/db/:dbTitle', async (req, res) => {
const {dbTitle} = req.params;
console.log(dbTitle);
try {
const tabTitleResult = await session.run(`MATCH (db:Database {Title: $dbTitle})-->(t:Table)-->(tv:TableVersion)
Where NOT (tv)<--(:InformationAsset)
RETURN db.Title as dbTitle, tv.Title as tabTitle Order By db.Title, tv.Title ASC`, {dbTitle});
const tabTitleArr = tabTitleResult.records.map(({_fields}) => {
return {dbTitle:_fields[0],tabTitle:_fields[1]};
});
res.render('table.ejs', { tabTitle: tabTitleArr});
//console.log(tabTitleArr)
} catch(e) {
console.log("Something went wrong", e)
}
});
everything from above displays nicely on this page...
table.ejs code
<table>
<tr>
<th>Database-Title</th>
<th>Table-Title</th>
</tr>
<% tabTitle.forEach(function (tabTitle){ %>
<tr>
<td><%= tabTitle.dbTitle %></td>
<td><%= tabTitle.tabTitle %></td>
<% }) %>
</tr>
</table>
Here's what doesn't work...
Instead of an unordered list of hyperlinks, I would prefer to have a dropdown select, however my code doesn't work when I try to use a form select option method to send the info/variable via req.body...
db.ejs code
<form method="post" action="/db">
<label>Database Name</label><br>
<select name="dbTitle" onchange="this.form.submit();">
<option selected disabled> -- select an option --
<% dbTitle.forEach(function(dbTitle){ %>
<option name="dbTitle" value="<%= dbTitle.dbTitle %>"><%= dbTitle.dbTitle %></option>
<% }) %>
</option>
</select>
</form>
(Note: I am aware of how strange the nested options seem, this is required to force the --select an option-- option to appear first, removing the nesting with only the one option with data does not help.
Also, you'll note that I'm adding name="dbTitle" on more than one element in a desperate attempt to make something work, I believe it should only be on the select element.
Last, I'm also trying to send any info/variable via value="<%= dbTitle.dbTitle %>.)
server.js code
app.post('/db/:dbTitle', async (req, res) => {
const {dbTitle} = req.body;
console.log(dbTitle);
try {
const tabTitleResult = await session.run(`MATCH (db:Database {Title: $dbTitle})-->(t:Table)-->(tv:TableVersion)
Where NOT (tv)<--(:InformationAsset)
RETURN db.Title as dbTitle, tv.Title as tabTitle Order By db.Title, tv.Title ASC`, {dbTitle});
const tabTitleArr = tabTitleResult.records.map(({_fields}) => {
return {dbTitle:_fields[0],tabTitle:_fields[1]};
});
res.render('table.ejs', { tabTitle: tabTitleArr});
//console.log(tabTitleArr)
} catch(e) {
console.log("Something went wrong", e)
}
});
From here, when I run and then select from the dropdown, I receive an error of Cannot POST /table, and nothing shows in my console.log(dbTitle);, so I'm assuming no variable is being sent from my form to the server.
From what I've gathered in using a form vs ul li hyperlinks, there are some differences where the form needs to have method="post", and the server needs to be app.post with req.body instead of req.params. Or maybe this is incorrect?
Thank you for any help you can share.
I figured it out, here's what I needed to do.
Everything was fine on my client db.ejs.
In my server.js, I needed to change app.post('/auradbtable/:dbTitle' to app.post('/auradbtable?:dbTitle'... change the '/' to '?'.
And using const {dbTitle}=req.body; is correct.

Not being able to delete an item from to-do list using Item.removeOne( ) or Item.findByIdAndRemove( ) methods of mongoose in NodeJS

I am making a to-do list using mongodb,node.js,express and EJS. The part where I am stuck is I am not being able to delete and item from the list and the database. The idea is that on clicking a checkbox, the item beside will be deleted. Here is a layout for the same,
[https://i.stack.imgur.com/83gMW.png][1]
I made the necessary changes in my EJS file to create a "/delete" route as given below :
<%for(var i=0;i<taskslist.length;i++){%>
<form action="/delete" method="POST">
<div class="item">
<input type="checkbox" name="checkitem" value="<%= taskslist[i]._id %> " onChange="this.form.submit()">
<p><%= taskslist[i].activity %> </p>
</div>
</form>
<%}%>
In my app.js folder, I also made the necessary changes by using findByIdAndRemove( ) method for mongoose :
app.post("/delete", function (req, res) {
const delitem = req.body.checkitem;
Item.findByIdAndRemove(delitem, function (err) {
if (err) {
console.log("Error");
} else {
console.log("Succesfully deleted" + delitem);
res.redirect("/");
}
});
console.log(delitem);
});
However, the item is not getting deleted from the database and an error is showing in terminal, even though the console.log(delitem) is working and returning the id of the item whose checkbox is selected.
I am also giving a screenshot of the output I am getting at the terminal:
[https://i.stack.imgur.com/hGEkn.png][1]
All the methods related to adding items and creating database are working. I don't understand where the error is. Please help me out with this.

how to do a MongoDB query and use it in a partial view

I have a collection called events that I am currently using in my footer partial, however, I would like to show only the ones with a date greater than today. I have achieved this on mongo shell like this:
db.events.find({date: {$gte: new Date()}})
I don't understand how to use this query in footer partial where I am running a loop to show the events, I would like to save this query in a variable that I can use in the loop, and it would be visible in every page of the website as they all have the footer.
To be able to use this collection in every page I am using this code in my app.js
// passing courses to every EJS template
app.use(function (req, res, next) {
Event.find({}, function (err, allEvents) {
if (err) {
console.log(err);
res.locals.events = [];
next();
} else {
res.locals.events = allEvents;
next();
}
});
});
and this is where I am doing my loop in footer partial
<div id="event-footer" class="col-12 mt-4 mt-md-5 text-center col-md-4 text-md-left">
<div class="">
<h4 class="pb-1 pb-md-5">PROXIMOS EVENTOS</h4>
<% var count = 0; %>
<% events.forEach(function(event) { %>
<% if (count <= 2 ) { %>
<div class="eventDisplay text-center text-md-left">
<h6><%=moment(event.date).format('dddd, D MMMM, h:mm a')%></h6>
<p> <%= event.name %> </p>
</div>
<% } %>
<% count++; }); %>
</div>
</div>
Try this ...
Event.find({date: {$gte: new Date()}}, function (err, allEvents) { ...

Iterate through a MongoDB object's data

I will start off with how my mongoDB data looks like:
_id : 5c5b450918cb2b121648ff7a
name : "dannondarko"
email : "dangilmail#gmail.com"
password : "$2a$10$3z5m1e9Pcfid72Q2GchCjeTD55/SsIxmtWr3I1ZiA.DX/KlpfTbdK"
date : 2019-02-06 20:35:21.973
__v : 0
posts : Array
0 : Object
note : "test for the user dannondarko"
date : "02/08/2019"
This is just a side project and most likely will never be live so don't worry about the security of me posting this data! As for how I am procession the code in my server code:
app.get('/:username', (req, res) => {
username = req.params.username.toLowerCase();
const collection = req.app.locals.collection;
collection.find({ name: username }).toArray(function (err, results) {
if (err) {
res.status(500).send("Error communicating with the DB.");
} else if (results.length > 0) {
console.log("Here are the results: " + results);
console.log({people: results});
res.status(200).render('profile', {posts: results, name: username});
} else {
next();
}
});
});
What I am doing with this code is say you head to the address bar '/dannondarko', it should find 'dannondarko' in the collection, which it does fine, and then the 'results' variable is the complete object that I posted above. What I am trying to do is just get the 'posts' data, such as the note and date.
The note and date is the only data I need, which will be sent to this .ejs file that should create a post (kind of like FB) that shows the users' notes and date of the post. Here is my .ejs file:
<h1 class="mt-4"><%= name %></h1>
<div class="container">
<br>
<% for(var i=0; i < posts.length; i++) { %>
<div class="container">
<label><%= posts[i].note %></label>
<div class="container">
<label><%= posts[i].date %></label>
</div>
</div>
<% } %>
</div>
I hope that's enough information. I believe my downfall is not knowing how to just extract the 'posts' array from MongoDB from a certain user and iterate through the objects and sending over the note and date to the .ejs.
The results is an array of documents and you render this array to ejs as posts. Now in your ejs file posts represent the array of documents, not the posts array. So if you want to loop through all results you should edit your code like this:
<% posts.forEach(post => { %>
<h1 class="mt-4"><%= post.name %></h1>
<div class="container">
<br>
<% post.posts.forEach(p => { %>
<div class="container">
<label><%= p.note %></label>
<div class="container">
<label><%= p.date %></label>
</div>
</div>
<% }) %>
</div>
<% }) %>
If i understand well your mongo model structure the above should help you.

Multiple mongoDB queries in one router.get method nodejs

I would like to have multiple queries in a single router method as follows in my index.js file,
router.get('/users', function(req, res) {
var db = req.db;
var users = db.get('users');
var col_name=req.query.colname;
var col_value=req.query.colvalue;
var query={};
query[col_name]=col_value;
console.log(col_name);
console.log(col_value);
console.log(query);
//using this i would like to populate my dropdown list
users.distinct('symbol',{limit: 10000},function(e, syms){
res.send('users', {
title: 'usersSym',
'usersSym': syms
});
});
// using this I would populate a table in html
users.find(query,{limit: 10000},function(e, docs){
res.render('users', {
title: 'Users',
'users': docs
});
});
});
And in my .ejs file I'm trying to do the following :
<html>
<head>
//drop down list populating with first query
<select id="selected" name="colvalue" >
<option value="">--Select--</option>
<% usersSym.forEach(function(usersym) { %>
<option value="<%= usersym.symbol %>"><%= usersym.symbol %></option>
<% }); %>
</select>
//Table populating with second query
<table >
<tr >
<th>Symbol</th>
<th>Order_id</th>
</tr>
<tr>
<% users.forEach(function(user) { %>
<td ><%= user.symbol %></td>
<td><%= user.order_id %></td>
</tr>
<% }); %>
</table>
</body>
</head>
</html>
But no luck. Wondering whether I'm going in right direction or not. If not please guide me in right way.
// 1st fetch symbol
users.distinct('symbol',{limit: 10000},function(e, syms){
// 2nd fetch [users]
users.find(query,{limit: 10000},function(e, docs){
res.render('users',
{
usersSym: syms,
users: docs
}
);
});
});
// P.S. do not forget to check on error on each callback
You can only send data back to the client once. One way would be to do all those DB queries in a sequence, and then send all the data. Another might be to do it your way, check the status of all DB queries, and if all are done, then send the data.

Resources