Node js for loop unable to access variable it print undefiend - node.js

I am using Node js and use sendmail function
i declare variable htmlVal i try to access outside but it print undefiend only
function myFunction(){
var htmlVal;
Report.find({}, function (err, reports) {
if (err) return res.status(500).send("There was a problem finding the users.");
for(let reportData of reports){
User.find({name:reportData.name}, function (err, users) {
if (err) return res.status(500).send("There was a problem finding the users.");
for (let item of users) {
htmlVal = `<table width="100%" cellpadding="2" cellspacing="0" style="border-style:solid; border-width:1px; border-color:#000000;">
<tr width="100%" cellpadding="2" cellspacing="0">
<th style="text-align:center; border:1px solid #000; padding:10px;border-right:1px solid #000">Name</th>
</tr>
<tr width="100%" cellpadding="2" cellspacing="0">
<td style="text-align:center;padding:10px;border:1px solid #000">`+item.name+`</td>
</tr>
</table>`;
}
});
}
});
console.log(htmlVal);
const sendmail = require('sendmail')({
silent:true, 
})
sendmail({
from: 'mymail#gmail.com',
to: 'mymail#gmail.com',
subject: 'Attendance of the Day',
html: htmlVal
}, function(err, reply) {
console.log(err && err.stack);
console.dir(reply);
})
}
I am using this htmlVal for send email but it send empty only
because i am unable to access htmlVal outside
how to access it outside

You need something like this.
let htmlVal;
async.series([
function(callback) {
Report.find({}, function (err, reports) {
if (err) return res.status(500).send("There was a problem finding the users.");
async.eachSeries(reports, function (value, key, callbackFE) {
User.find({name:value.name}, function (err, users) {
if (err) return res.status(500).send("There was a problem finding the users.");
for (let item of users) {
htmlVal = `<html></html`;
}
callbackFE();
});
}, function (err) {
if (err) console.error(err.message);
callback(null, htmlVal);
});
});
}], function(err, htmlVal) {
console.log(htmlVal);
const sendmail = require('sendmail')({
silent:true,
})
sendmail({
from: 'mymail#gmail.com',
to: 'mymail#gmail.com',
subject: 'Attendance of the Day',
html: htmlVal
}, function(err, reply) {
console.log(err && err.stack);
console.dir(reply);
});
});

The main issue you're encountering is that Report.find is an async function, as well as User.find, but you're sending your email synchronously.
Or in other words you're sending the email before the report is rendered.
In this case, since you have a second async query and loop inside of Report.find you're probably going to want to use the async library and then compose your async methods together.
Read about the eachSeries function and then render your html inside of the Reports.find callback instead of immediately after calling Report.find.

If my understanding is correct, you are using the variable htmlVar even before a value is getting assigned to it.
sendmail function will get called before the value is assigned to htmlVar because Report.find is a asynchronous function. Either you need to move the sendmail function call inside the callback function or use async/await.

Related

How to render mongodb data to ejs file?

I am learning how to display mongodb data in html, but the code cannot work when I learn from network. I want to create the button to change the page to view data, and I don't know how to render data to ejs.
I try to find some method to solve the problem in network, but most of them are not the problem which I get.
code of find data
app.post('/viewdata', function (req, res) {
res.render('staffDisplay');
try{
MongoClient.connect(uri, function(err, client) {
if(err) {
console.log('Error occurred while connecting to MongoDB Atlas...\n',err);
}else{
var game=[];
var collection = client.db("GameDB").collection("Game");
var result = collection.find();
result.forEach(function(error,result){
if(error){
console.log(error);
}else{
if(result != null){
game.push(result);
}else{
console.log(game);
res.render('views/staffDisplay.ejs',{game:result})
}
}
})
console.log('show');
client.close();
}
});
}catch(ex){
throw new Error(ex.toString());
}
});
display.ejs
//skip the html code
<ul>
<% for(var i=0;i<=game.length;i++) {%>
<li><%=game[i].gName%></li>
<li><%=game[i].gDesc%></li>
<li><%=game[i].gDate%></li>
<%}%>
</ul>
the result is display 'game is not define', how can I do?
Can you try to remove this res.render('staffDisplay'); on the first part then replace res.render('views/staffDisplay.ejs',{game:result}) with this res.render('staffDisplay.ejs',{game:result})

implement voting system using node.js, mongodb and ejs

I'm pretty new to node.JS and ejs. I have searched how to implement real-time voting system like Stackoverflow voting using the mentioned technology. But I don't have any Idea how to do it.
here is my front-end code:
<% for (var i = 0; i < gamePasses.length; i++) {%>
<form dir="ltr">
<tr>
<td>
<%= i + 1 %>
</td>
<td>
<%= gamePasses[i].teamName %>
</td>
<td>
<%= gamePasses[i].password %>
</td>
<td>
<%= gamePasses[i].votes %>
</td>
<td onclick="vote('<%= gamePasses[i].password %>')">
<i class="material-icons">
thumb_up
</i>
</td>
</tr>
</form>
<% } %>
<script>
function vote(pass) {
$.post('/user/vote/' + pass)
}
function passSubmit() {
$.post('/user/gamePassword', {
gamePassword: $('#password').val()
}).done(() => {
window.location.assign('/user/scoreboard')
})
}
</script>
back-end code:
uter.post('/vote/:gamePassword', (req, res) => {
gamePassword = req.params.gamePassword;
gamePassModel.find({password: gamePassword}, (err, result) => {
if (err) console.log(err)
else {
result.votes += 1
result.save((err) => {
if (err) console.log(err)
})
}
})
})
This code problems are:
Doesn't support real-time voting
each user can votes many times
I will be thankful if anyone could help me
For prohibit multiple vote from the same user, you can think something like this. Didn't test it but the logic should works.
uter.post('/vote/:gamePassword', (req, res) => {
gamePassword = req.params.gamePassword;
//you can retrieve the voter id the way you want (in this case I assumed the query string contains it)
let voterId = req.query.voterId
gamePassModel.find({password: gamePassword}, (err, result) => {
if (err) console.log(err)
else {
//votes in not a number anymore. Now it's an array of IDs, you have to change the model (in Mongoose, I suppose)
if(~result.votes.indexOf(voterId)) {
result.votes.push(voterId)
//I'm pretty sure you cannot call save() like you do on this object, but this is not part of the question here, check the mongoose docs
result.save((err) => {
if (err) res.json({error:err});
})
} else res.json({ error: "you already voted" });
}
})
})
Now when you want to get the total vote of something, you have to make a count query (still assuming mongodb/mongoose https://mongoosejs.com/docs/api.html#model_Model.count)

Node.js + Mongoose - Not getting data every time

I made a drop-down which gets its links from some data I get with mongoose.
However its not persistent. With the exact same code, I don't always get my data for the links.
(It's like this for all my things actually, but my drop-downs are simple)
My drop-down (made with EJS and bootstrap)
<div class="dropdown-menu" aria-labelledby="navdrop">
<% schools.forEach((school) => { %>
<%= school.name %>
<% }); %>
</div>
(Sorry for the shitty format above, the editor wouldnt let me make it better).
This is my route for handling my index page.
server.get('/',
async function(req, res) {
let schools = await schoolService.getAll();
res.render('public assets/pages/index', {
page_title: "Langaming.dk - Index",
schools: schools
});
}
);
This is my schoolService.getAll();
"getAll": () => {
return new Promise(function(resolve, reject){
School.find({}, function (err, schools) {
if (err)
return reject(err)
else
return resolve(schools)
});
})
}
I will try and explain it a bit better. When I go onto my page, sometimes the links show up, and other times they don't. (Mostly they don't). It's the same code all the time.
The project is running express for route handling.
Why does this happen?
Might be that for some reason your database doesn't have documents (are you wiping out data between requests?) and it's not going to throw an error just because of it.
"getAll": () => {
return new Promise(function(resolve, reject){
School.find({}, function (err, schools) {
if (err)
return reject(err)
if (!schools) {
console.log('there are no documents');
return reject();
}
else
resolve(schools)
});
})
}

Mongoose (express, node, mongo) scope issue with findOne()

I'm having an issue with what I believe is the scope of a variable in mongoose. My code is this:
var blogUserId;
blogs.forEach(function(blog, index) {
User.findOne({'username': blog.username}, function(err, user) {
blogUserId = user._id;
console.log(blogUserId);
});
console.log(blogUserId);
Blog.find({'title': blog.title}, function(err, blogs) {
if (!err && !blogs.length) {
console.log(blogUserId);
Blog.create({title: blog.title, author: blogUserId, body: blog.body, hidden: blog.hidden});
}
if (err) {
console.log(err);
}
});
});
This is part of a seed file just for development, but I'm rather confused why it wouldn't be working. blogs is just an array of objects to load into the collection. I've searched on all similar answers but I haven't found a correct answer that would explain this.
Your blogUserId is not set when your Blog.find() is called. You'd have to nest it differently, something like this:
var blogUserId;
blogs.forEach(function(blog, index) {
User.findOne({'username': blog.username}, function(err, user) {
blogUserId = user._id;
console.log(blogUserId);
Blog.find({'title': blog.title}, function(err, blogs) {
if (!err && !blogs.length) {
console.log(blogUserId);
Blog.create({title: blog.title, author: blogUserId, body: blog.body, hidden: blog.hidden});
}
if (err) {
console.log(err);
}
});
});
});
I haven't tested it so I'm not sure that there are no other errors in your code but was definitely a problem that you were calling Blog.find that expected the blogUserId to be set possibly before it was set in the User.findOne callback.
It can be written in a more readable way with named callbacks.
When working in Node you need to keep in mind that you are working in an asynchronous environment.

Trying to get data to display in a table

I'm working on my first node project using express and sequelize, and I'm not understanding how the page rendering works
I have the following function in my one of my models(sequelize):
getGreetings: function (req, res) {
Greeting.findAll({
}).then(function (data) {
console.log('Returned data for greetings: ' + data);
res.send({greetings:data});
})
}
Here is my route:
var Greeting = require('../models/greetings');
router.get('/', function(req, res) {
res.render('index', function(){
Greeting.getGreetings(req, res);
});
});
and my ejs table I want to display the data in:
<tbody>
<% for(var i=0; i < greetings.length; i++) { %>
<tr>
<td><%= greetings[i].name %></td>
<td><%= greetings[i].message %></td>
</tr>
<% } %>
</tbody>
This isn't displaying any of the html, but rather echoing out the json data. Can someone help explain why my html table isn't being populated?
but rather echoing out the json data.
This is because getGreetings() is always setting that as the response, by using res.send():
res.send({greetings:data});
To provide greetings to your view, you'll have to instead provide the data within the locals given to res.render():
res.render('index', { greetings: data });
The two methods don't cooperate with each other. Each is defined to end the response itself, so you'll only be able to use one per response.
If you revise getGreetings to return the promise created by .findAll():
getGreetings: function (req) {
return Greeting.findAll({
// ...
});
}
Then, the route handler can bind to it and decide how to make use of the result itself – whether it should use res.send() or res.render():
var Greeting = require('../models/greetings');
router.get('/', function(req, res) {
Greeting.getGreetings(req).then(function (greetings) {
res.render('index', { greetings: greetings });
});
});

Resources