node.js : How do I pass in multiple variables into a view? - javascript

I'm trying to create a webpage where there is an instance of all the current Projects I am working on, on the left, so I'd need a .forEach() function in order to loop through all of them in order to display it, but on the other side, I need to display the information that is currently selected.
Please first take a look at my code block so I can try to explain the thought process behind what I was trying to do.
So I didn't have any problems selecting the information of the single project that I needed to display on this webpage. I used the .findOne() function in order to pick out the information that I needed.
The problem that I'm facing is that I also need to pass a var that's connected to the .find() function in order to pass through all of the elements of the database. The way I went about this is that I thought I would be able to set the definition of allProjects by manually running the .find() function, and then returning it, thus assigning Projects.find() to allProjects.
app.get('/projects/:url', (req, res) => {
Projects.findOne({ Url: req.params.url }, (err, foundProject) => {
if (err) {
console.log(err);
} else {
res.render('show', {
foundProject: foundProject,
allProjects: Projects.find({}, (err, allProjects) => {
if (err) {
res.send('error');
} else {
return allProjects;
}
})
});
}
});
});
I thought that by returning allProjects and then also having that assigned to allProjects, i'd be able to use the allProjects variable in my show.ejs page.
Unfortunately, I'm getting an error 'allProjects.forEach() is undefined' which leads me to believe that in the app.js where I am defining allProjects, it's not being assigned the correct value that I want it assigned.

It looks like you're expecting return allProjects to do something, but that's actually ignored. Unless you have a callback function you can call, that will go into the void and never be seen by anyone. This is true of virtually all callback functions. They do not care what value that function returns because it's never relevant, what they want is the future value which comes through the callback given to this function.
In other words it plays out like this:
asyncFunctionTakingCallback(function(cb) {
cb(null, value); // This is the important value!
return value; // Nobody cares about this value. Don't even bother.
});
To fix that you need to move the render call inside of the inner-most callback function:
app.get('/projects/:url', (req, res) => {
Projects.findOne({ Url: req.params.url }, (err, foundProject) => {
if (err) {
console.log(err);
// Return here to avoid another level of indentation below
return;
}
Projects.find({}, (err, allProjects) => {
if (err) {
res.send('error');
} else {
res.render('show', {
foundProject: foundProject,
allProjects:
});
}
});
});
});
Now that's still a dizzying amount of code and the nesting here is getting completely out of control even though this is relatively simple Node code.
For comparison here's a version that uses async functions:
app.get('/projects/:url', async (req, res) => {
let foundProject = await Projects.findOne({ Url: req.params.url });
res.render('show', {
foundProject: foundProject,
allProjects: await Projects.find({})
});
});
There's really not much to it this way. What await does is basically stall out on that line and wait for the promise to get resolved or produce an error. Any errors produced should be captured with try { ... } catch as usual.

Related

Reading data off JSON file and saving it to database

I am trying to upload a JSON file to my express app, read data from the JSON file and save it as a new document in my MongoDB database.
The documents get saved but the fields don't get added, and that is due to the fields being undefined for some reason, How do I fix this?
When I console.log(obj) i can see the data but when I console.log(obj['DataSource_Name']) for example I get undefined why?
app.post("/upload-config", uploads.single("txt"), async (req, res) => {
// Read the file and send to the callback
fs.readFile(req.file.path, handleFile);
let obj;
// Write the callback function
function handleFile(err, data) {
try {
obj = JSON.parse(data);
console.log(obj["DataSource_Name"]);
} catch (err) {
console.log(err);
}
}
const config = new ConfigM({
DataSource_Name: obj.DataSource_Name,
DataSource_ID: obj.DataSource_ID,
DataSource_Type: obj.DataSource_Type,
DataSource_ICON: obj.DataSource_ICON,
DoubleQuotes_Text: obj.DoubleQuotes_Text,
Delimeter: obj.Delimeter,
Field_list: obj.Field_list,
});
try {
await config.save();
res.send(obj);
} catch (err) {
console.error(err);
}
});
obj is only going to be defined when fs.readFile is ready which is NOT as soon as you called it. That happens asyncronously. So after fs.readFile() is called, the code just continues executing, creating the config object and obj is still not defined.
If you would like to use callback functions as you have here, you probably want your config creation, config.save() and res.send(obj) to be part of the readFile callback. Maybe to try before refactoring you could simply move the closing } right after the } of your final catch.
app.post("/upload-config", uploads.single("txt"), async (req, res) => {
// Read the file and send to the callback
fs.readFile(req.file.path, handleFile);
let obj;
// Write the callback function
function handleFile(err, data) {
try {
obj = JSON.parse(data);
console.log(obj["DataSource_Name"]);
} catch (err) {
console.log(err);
}
const config = new ConfigM({
DataSource_Name: obj.DataSource_Name,
DataSource_ID: obj.DataSource_ID,
DataSource_Type: obj.DataSource_Type,
DataSource_ICON: obj.DataSource_ICON,
DoubleQuotes_Text: obj.DoubleQuotes_Text,
Delimeter: obj.Delimeter,
Field_list: obj.Field_list,
});
try {
await config.save();
res.send(obj);
} catch (err) {
console.error(err);
}
}
});
I am not certain what you precisely are doing by reading the file locally while you got it from the post. Be aware that you will want a lot more security guards all over the place and not accepting path names from the request to decide the path name on your server. But I guess that's all a topic for another stackoverflow question :)

How to save mongodb findone value to a javascript variable to use it later on nodejs

I have spent tow day to solve this problem but got nothing. All the answers in stackoverflow didn't work for me.
here is my code
const userCredential = require("./db").db().collection("api");
now I want to retrieve a doc like API to save it on a variable
const api = userCredential.findOne({username:'admin'}, (err, doc)=> (doc.username))
and now I want to use the variable to anywhere in the file. But it returned undefined.
if I try this one
let api;
userCredential.findOne({username:'admin'}, (err, doc)=> (api = doc.api))
also return undefined because of nature of Asynchronous.
How can I save in to a variable?
Please try below code
function query(username) {
console.log('query');
Test.findOne({ username: username }, function (err, result) {
console.log('findOne callback');
if (err) {
return console.error(err);
} else {
console.log(`result: ${result}`);
return ${result};
}
});
}

setTimeout inside a callback failed to remove a mongodb document [duplicate]

I am using stripe for payments in my app, I want to create a receipt document in my own database after a succesful transaction
My code:
Meteor.methods({
makePurchase: function(tabId, token) {
check(tabId, String);
tab = Tabs.findOne(tabId);
Stripe.charges.create({
amount: tab.price,
currency: "USD",
card: token.id
}, function (error, result) {
console.log(result);
if (error) {
console.log('makePurchaseError: ' + error);
return error;
}
Purchases.insert({
sellerId: tab.userId,
tabId: tab._id,
price: tab.price
}, function(error, result) {
if (error) {
console.log('InsertionError: ' + error);
return error;
}
});
});
}
});
However this code returns an error:
Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.
I am not familiar with Fibers, any idea as to why this is?
The problem here is that the callback function which you pass to Stripe.charges.create is called asynchronously (of course), so it's happening outside the current Meteor's Fiber.
One way to fix that is to create your own Fiber, but the easiest thing you can do is to wrap the callback with Meteor.bindEnvironment, so basically
Stripe.charges.create({
// ...
}, Meteor.bindEnvironment(function (error, result) {
// ...
}));
Edit
As suggested in the other answer, another and probably better pattern to follow here is using Meteor.wrapAsync helper method (see docs), which basically allows you to turn any asynchronous method into a function that is fiber aware and can be used synchronously.
In your specific case an equivalent solution would be to write:
let result;
try {
result = Meteor.wrapAsync(Stripe.charges.create, Stripe.charges)({ /* ... */ });
} catch(error) {
// ...
}
Please note the second argument passed to Meteor.wrapAsync. It is there to make sure that the original Stripe.charges.create will receive the proper this context, just in case it's needed.
You might want to take a look at the docs for http://docs.meteor.com/#/full/meteor_wrapasync.

Javascript return is not returning anything

I have a NodeJS server and a helper function that is making a HTTP request, but when I make the call for the function, it comes up as undefined. The call is being made in a callback, so I don't think that there is a problem with the Async part of it.
This is the server.js
console.log(dictionary('wut'));
And this is the function dictionary.
if(dictionary[word]===undefined){
request({
url:"https://mashape-community-urban-dictionary.p.mashape.com/define?term="+word,
headers:{
'X-Mashape-Key':'KEY HERE',
'Accept':'text/plain'
}
}, function(err, response, body){
if(!err && response.statusCode==200){
var info = JSON.parse(body);
console.log(info.list[0].definition);
return info.list[0].definition;
}
});
} else {
return dictionary[word];
}
Where word is the word that is being passed to the function.
Edit: I forgot to mention that the dictionary function has
module.exports = function(word){
The return statements are supposed to give the module the value from the callback. Sorry about that, that's kind-of important information.
You're going to want to use a callback method with your helper method.
So your helper defintion would look like this:
function dictionary(word, callback) {
request({}, function(err, res, body) {
if (err) {
return callback(err);
}
callback(null, body);
});
}
And your call would become:
dictionary('wut', function(err, result) {
if (err) {
return console.error('Something went wrong!');
}
console.log(result);
});
This is obviously a very simple implementation, but the concept is there. Your helpers/ modules/ whatever should be written to accept callback methods that you can then use to bubble up errors and handle them in the appropriate place in your application. This is pretty much the standard way to do things in Node.
Here is how you could call your helper using a simple Express route:
router.route('/:term')
.get(function(req, res) {
dictionary(req.params.term, function(err, result) {
if (err) {
return res.status(404).send('Something went wrong!');
}
res.send(result);
});
});
From my viewpoint, it looks like that request library you're using was designed asynchronous. This means that you should handle the data inside of the callback function.
For instance:
function handle(data) {
// Handle data
console.log(data)
}
if(dictionary[word]===undefined){
request({
url:"https://mashape-community-urban-dictionary.p.mashape.com/define?term="+word,
headers:{
'X-Mashape-Key':'KEY HERE',
'Accept':'text/plain'
}
}, function(err, response, body){
if(!err && response.statusCode==200){
var info = JSON.parse(body);
console.log(info.list[0].definition);
handle(info.list[0].definition)
}
});
} else {
handle( dictionary[word] )
}
You didn't provide enough information for me to set this up correctly. But hopefully this gives you an idea on what you need to do.
To elaborate on why you should set it up this way:
The request function appears to be asynchronous, so keep it how it was designed.
You're returning inside of the callback, so your outer dictionary function isn't getting that returned data, the callback is.
Since the request function was designed async, there isn't a way to return the data to the dictionary without forcing it to be synchronous (which isn't advised). So you should restructure it to be handled inside of the callback.
(Another little note, you should use typeof dictionary[word] === "undefined", because I believe JavaScript sometimes throws an error otherwise.)

Getting data from mongodb/mongoose using predefined functions

This is how I am currently getting data from mongodb:
users.get(base_URL, (req, res) => {
UserModel.find({}, (err, docs) => {
res.render("Users/index", {
title: "All Users here",
user_list: docs
});
});
});
Now, as you can see this is an express application. What I would like, is to simple call a function so that I can get the value from the docs variable inside the mongodb model callback. How do I do this, ideally, I want to see something like this:
users.get(base_URL, (req, res) => {
res.render('<some_jade_file_here>', {
title: "Yes, got it right",
user_list: getAllUsers();
});
});
Ideally, I just want to call a function. How can I do this, since having to put render inside of a mongodb call is a problem, since you may want to query a bunch of things from the database, and it might not even be just one database. I'm struggling a little since I'm not all that used to callbacks.
Any help would be deeply appreciated. If you're wondering about this syntax () => {}, thats just an anonymous function in typescript.
You can't do it without callbacks, but you can use an async flow control library like async to help manage the nest of callbacks. In this case you probably want to use async.parallel.
Using that you can do something like:
users.get(base_URL, (req, res) => {
var data = {
title: "Yes, got it right"
};
async.parallel([
(callback) => {
UserModel.find({}, (err, docs) {
data.user_list = docs;
callback(err);
});
},
(callback) => {
// Other query that populates another field in data
}
], (err, results) => {
// Called after all parallel functions have called their callback
res.render('<some_jade_file_here>', data);
});
});

Categories