Node.js + Mongoose - Not getting data every time - node.js

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)
});
})
}

Related

How to do 2 or more "FIND" (mongo) request in node in the same route?

I´m new to Node, Mongo and ReactJS, and I´m trying to show all the documents in my collections in the same page. But I don´t know how to call the FIND methods and which what route use, because it has to be shown in the same page. This is the code I have so far.
app.get("/home",(req,res)=>{
JobModel.find({},(err,result)=>{
if(err){
res.json(err);
}else{
res.json(result);
}
});
});
app.get("/home",(req,res)=>{
SchoolModel.find({},(err,result)=>{
if(err){
res.json(err);
}else{
res.json(result);
}
});
});
Also, like the information from the schools and jobs are almost the same (except for the name, they both have date and desciption, just the name attribute changes) the information of the jobs are duplicated but with diferent style and with no name shown(because I changed the style between them to identificate them)
You can also use the new async syntax to solve.
Your code would be like this.
app.get("/home", async (req, res) => {
const jobData = await JobModel.find().exec();
const schoolData = await SchoolModel.find().exec();
res.json({
jobData,
schoolData
})
});
There may be many approache that you have, I think a simple one is to use promise.all function in node.js
const promise1 = JobModel.find({}).exec();
const promise2 = SchoolModel.find({}).exec();
Promise.all([promise1, promise2]).then(values =>
{
console.log(values[0]) // job docs
console.log(values[1]) // school docs
res.json(values)
}
).catch(err => console.error(err))

Better way to find multiple collections with Mongoose for one render

I am in the process of learning Node, Express and Mongoose and creating a web application. Sometimes, in one page, I need to display data from two or more of my collections. Although it works just fine, right now I use a bunch of nested if statements and have realized that the code has become very messy looking.
Example:
app.get("/jobs/:id/edit", function(req, res){
Job.findById(req.params.id, function(err, foundJob){
if (err){
console.log(err)
} else {
User.find({}, function(err, users){
if(err){
console.log(err);
} else {
Client.find({}, function(err, clients){
if(err) {
console.log(err);
} else {
let start_date = foundJob.start_date;
let end_date = foundJob.end_date;
start_date = moment(start_date).format("MM-DD-YYYY");
end_date = moment(end_date).format("MM-DD-YYYY");
// Redirect
res.render("edit_job", {job: foundJob, users: users, clients: clients, start_date, end_date});
}
});
}
});
}
});
});
This example is for a page that displays information from just three collections. Is there a better way to write this kind of code? I feel like using a table of collection names and using a for loop might work, but I am unsure how I would write that.
As an update, I tried the following logic, but it did not work:
app.get("/", function(req, res){
let collections = [Client, User, Ticket, Job];
let endCollections = [];
for (let i = 0; i < collections.length; i++){
collections[i].find({}, function(err, foundCollection){
if (err) {
console.log(err);
} else {
endCollections[i] = foundCollection;
}
})
}
res.render("dashboard", {clients: endCollections[0]});
No matter what I do, endCollections[i] remains undefined even though I have it set to be foundCollection, which is not undefined.
Thanks.
in the for-loop, you're executing an asynchronous block of code (collection.find()), so javaScript will not wait till this asynchronous code executed then do the next block of code which is the render, that's why you got an empty array
you need to use async/await to force javaScript to wait until the asynchronous block of code executed, then do the rest
just add async to the main function to be able to use await inside this function
something like this
app.get("/", async function(req, res){ // <== note the async keyword here
let collections = [Client, User, Ticket, Job];
let endCollections = [];
for (let i = 0; i < collections.length; i++){
await collections[i].find({}, function(err, foundCollection){ // <== note the await keyword here
if (err) {
console.log(err);
} else {
endCollections[i] = foundCollection;
}
})
}
res.render("dashboard", {clients: endCollections[0]});
hope it helps

Site with node js and express / ejs does not always load images

I set up a website that basically uses Nodejs to fetch the image and after that sends it to ejs to display on the page, what happens is that sometimes the image appears and sometimes it looks like the website loads before the image can be loaded by the node.
I left the two ways I tried, one commented and the other that was the last one I tried.
This is app.js
function retornaImagens(id){
let imagens= {
assPacUrl: ''
}
/*
if (fs.existsSync(`${__dirname}\\arquivos\\consultas\\${id}\\assPac.png`)) {
fs.readFile(
`${__dirname}\\arquivos\\consultas\\${id}\\assPac.png`, 'base64',
(err, base64Image) => {
if (err) {
console.log(err)
} else {
imagens.assPacUrl = `data:image/png;base64, ${base64Image}`
}
}
)
}
*/
fs.access(`${__dirname}\\arquivos\\consultas\\${id}\\assPac.png`,(err)=>{
if(err){}else{
fs.readFile(
`${__dirname}\\arquivos\\consultas\\${id}\\assPac.png`, 'base64',
(err, base64Image) => {
if (err) {
console.log(err)
} else {
imagens.assPacUrl = `data:image/png;base64, ${base64Image}`
}
}
)
}
})
return imagens;
}
app.get('/consultas/:id/termo',(req,res)=>{
var imagens = retornaImagens(req.params.id);
Consulta.findOne({link:`/consultas/${req.params.id}/login`}).populate('medico paciente').exec((err,consulta)=>{
if(err){
console.log(err)
}else{
console.log(imagens)
res.render('consulta/termo',{consulta:consulta,id:req.params.id,imagens})
}
})
})
This is the ejs file
<img src="<%= imagens.assPacUrl %>">
If you have tips to make the code cleaner and consume less memory, please send me.
The problem was in the loading time, it was taking longer to load the image so the program continued and rendered the website empty, adding a settimeOut.
function enviaTermo(data){
Consulta.findOne({link:data.link}).populate('medico paciente').exec((err, consulta) => {
if (err) {
console.log(err)
} else {
console.log(imagens)
io.to(consulta._id).emit('termo', { consulta: consulta, imagens: imagens })
}
})
}
setTimeout(() => {
enviaTermo(data)
}, 450);

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})

ApostropheCMS Contact us form with attachment

I am new to Apostrophe and trying to create a contact us form with file attachment in Apostrophe by following the tutorial.
https://apostrophecms.org/docs/tutorials/intermediate/forms.html
I have also created the attachment field in my index.js and it works fine from the admin panel.
Now, I am trying to create my own html for the form with file submission.
// in lib/modules/contact-form-widgets/public/js/always.js
apos.define('contact-form-widgets', {
extend: 'apostrophe-widgets',
construct: function(self, options) {
self.play = function($widget, data, options) {
var $form = $widget.find('[data-contact-form]');
var schema = self.options.submitSchema;
var piece = _.cloneDeep(self.options.piece);
return apos.schemas.populate($form, self.schema, self.piece, function(err) {
if (err) {
alert('A problem occurred setting up the contact form.');
return;
}
enableSubmit();
});
function enableSubmit() {
$form.on('submit', function() {
submit();
//I can access file here
// console.log($form.find('file'))
return false;
});
}
function submit() {
return async.series([
convert,
submitToServer
], function(err) {
if (err) {
alert('Something was not right. Please review your submission.');
} else {
// Replace the form with its formerly hidden thank you message
$form.replaceWith($form.find('[data-thank-you]'));
}
});
function convert(callback) {
return apos.schemas.convert($form, schema, piece, callback);
}
function submitToServer(callback) {
return self.api('submit', piece, function(data) {
alert("I AM AT SUBMIT API ")
if (data.status === 'ok') {
// All is well
return callback(null);
}
// API-level error
return callback('error');
}, function(err) {
// Transport-level error
alert("I AM HERE AT API ERROR")
return callback(err);
});
}
}
};
}
});
//and my widget.html is
<div class="form-group">
<input name="custom-file" type="file">
</div>
When I run this I get following errors
user.js:310 Uncaught TypeError: Cannot read property 'serialize' of undefined
at Object.self.getArea (user.js:310)
at Object.self.getSingleton (user.js:303)
at Object.convert (user.js:686)
at user.js:164
at async.js:181
at iterate (async.js:262)
at async.js:274
at async.js:44
at setImmediate.js:27
at runIfPresent (setImmediate.js:46)
My question is, how do I handle file submission? Is there any better approach for this?
This is much easier to do using the apostrophe-pieces-submit-widgets module, which allows you to define a schema for what the user can submit. You can include a field of type attachment in that, and this is demonstrated in the README.

Resources