Unable to render user object to EJS view header.ejs - node.js

I am new to EJS and Node.js. I have been struggling to send the user object data to ejs template page. Actually, I want to display the current user's name who logged into the portal.
This is some portion of app.js
app.use(function(req,res,next){
res.locals.success_msg=req.flash('success_msg');
res.locals.error_msg=req.flash('error_msg');
res.locals.not_registered_user_msg=req.flash('not_registered_user_msg');
res.locals.authdata = firebase.auth().currentUser;
next();
});
//Get user info
app.get('*',function(req,res,next){
if(firebase.auth().currentUser!=null){
var id = firebase.auth().currentUser.uid;
var profileRef = fbRef.child('users');
var query = profileRef.orderByChild('uid').equalTo(id);
query.once('value', function(snap){
var user = JSON.stringify(snap.val());
console.log('User data is: '+user);
res.locals.user = user;
});
}
next();
});
Here is the header.ejs where I want to retrieve and display current user's name:
I am able to enter into the if condition., and able to print the phrase "Welcome,". But unable to get the actual user data, it simply showing empty space like this
please suggest me, where did i gone wrong here! Thanks in advance.
As per sugegstion i am adding logs from console here. I am getting user data from firebase including child id as marked here.

This doesn't look right to me:
var user = JSON.stringify(snap.val());
console.log('User data is: '+user);
res.locals.user = user;
You're converting the user object into a JSON string. Try this instead:
var user = snap.val();
console.log('User data is: ' + JSON.stringify(user));
res.locals.user = user;
I'm not really familiar with Firebase but I'd also guess that once is asynchronous, so you'd need to wait before calling next, a bit like this:
if (firebase.auth().currentUser != null) {
// ... This bit the same as before ...
query.once('value', function(snap) {
var user = snap.val();
console.log('User data is: ' + JSON.stringify(user));
res.locals.user = user;
next();
});
}
else {
next();
}
If that still doesn't work, could you include the output of the console logging in your question?

From reading over the express documentation, it seems that the arguments passed into the request handler are req and res.
You do not get the next function as an argument. With that said, you need to change your app.get to an app.use which is where you get the actual next function.
app.use((req, res, next) => {
if (firebase.auth().currentUser) {
const { uid } = firebase.auth().currentUser
const profileRef = fbRef.child('users')
const query = profileRef.orderByChild('uid').equalTo(uid);
query.once('value', snap => {
const user = JSON.stringify(snap.val())
res.locals.user = user
})
}
next()
})

Related

How to use variables from app.post in order to find and match them to database in app.get?

Hey I'm trying to use variables from my app.post in app.get but I'm getting an error that variables are not defined.
basically, I'm receiving data from API calls (when the user is submitting an input in a form),
then filtering the data to relevant data I want to display and store this data in the mongoose database.
now the problem is when I'm trying to find data in app.get from my DB and equal it to the variables from the app.post (in order to render the data on a relevant page [using ejs]) then I'm getting the error >>> asin is not define
what am I missing here? please see my code below
app.get("/", function(req, res) {
Asin.findOne({asinId:asin},function(err){
if(!err){
res.render("home");
}
});
});
app.post("/", function(req, res) {
//SOME API LOGIC//
https.get(url,options,function(response) {
//DATA RCIEVED//
response.on("end",function(){
const asinData = JSON.parse(data);
const asin = asinData.products[0].asin;
const parentAsin = asinData.products[0].parentAsin;
const category = asinData.products[0].categoryTree[1].name;
const subCategory = asinData.products[0].categoryTree[0].name;
const fbaFee = asinData.products[0].fbaFees.pickAndPackFee/100;
const newAsin = new Asin({
asinId:asin,
parentAsinId:parentAsin,
categoryId:category,
subCategoryId:subCategory,
feesId:fbaFee
});
newAsin.save(function(err){
if(!err){
res.redirect("/");
}else{
console.log(err);
}
});
Well, all I had to do is create an empty var for each const before the app.get, that way I could use the variable in both routs.

I'm trying to create a global variable in express (also using mongoose), but the app keeps deleting the variable when the page is refreshed

I'm working on an e-commerce project. I'm trying to create a shopping cart within the app so that people don't accidentally access another user's data in the Mongo database. To do this, I tried setting up a variable as res.locals.cart. This didn't work because I found out from the docs that res.locals expires in each new page.
My next idea was to create an anonymous shopping cart each time app.js started and store it in the global app.locals object. This does work, and in the following code, you can see it returns the model of the shopping cart. But after that, it's undefined as soon as I refresh or go to a new page as seen by console.log. Why is it doing that? How can I make it so that my data stays across the whole app? And I need it to be a variable, so that it changes for each new user. If there are also any NPM packages that solve this problem, that would be helpful to know.
app.locals.cart = Cart.create({}, function (err, newCart) {
if (!err) {
console.log(newCart);
return newCart
}
});
app.get('/cart', function (req, res) {
console.log(app.locals.cart);
res.render('cart')
});
💡 This is not the best practive, but, if you still want to do it, than this is an example code you can see:
const express = require('express');
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.locals.cart = [];
const item = {
id: 1,
name: 'Testing'
}
const addToCart = function(req, res, next) {
const { username } = req.body;
// req.body.username just for identifier
// you can change it with user id from token or token or whatever you want
if(typeof app.locals.cart[username] === 'undefined') {
app.locals.cart[username] = [];
}
// add item to cart by username / identifier
app.locals.cart[username].push(item);
next();
}
// or if you want to use this add to global, than you can use this middleware
// and now, you can see
// app.use(addToCart);
app.post('/', addToCart, (req, res, next) => {
// console.log
const { username } = req.body;
console.log(app.locals.cart[username])
res.send(app.locals.cart[username]);
})
app.get('/:username', (req, res, next) => {
const { username } = req.params;
console.log(app.locals.cart[username]);
res.send(app.locals.cart[username]);
})
app.listen(3000, () => {
console.log('Server is up');
})
I hope it's can help you 🙏.
I think the way you are trying is not a best practice.
Instead of using the locals try a different approach.
Creating a cart for each user in the database will be better.
You can link the card with the user. And whenever a user makes a request you fetch the cart from DB and do whatever you want.
To do that, you can add a user property to the Cart Schema. Whenever a user signs up, create a cart for it in the DB. When the user checkouts the cart, save the products in the card as another Document, let say an Order in the Orders Collection and clear the cart for future usage.
QUICK DIGEST:
Store any data from Mongoose onto a variable on your middleware and then have that variable read by app.locals or res.locals. The reason for this is because app.locals is changing and your middleware variable isn't, which lets it be read the same way every time. Example:
res.locals.data = middleware.var;
//or
app.locals.data = middleware.var;
Model.findById("model-id", function (err, noErr) {
if (!err) {
middleware.var = noErr //data retrieved from callback being passed into middleware.var
}
});
Here's what I discovered. My code didn't work because Express refuses to store data directly from a Mongoose function. I discovered this by console.logging the data inside the function and again outside it. I'm not sure why Express refuses to take data this way, but that's what was happening.
Cart.create({}, function (err, newCart) {
if (!err) {
app.locals.cart = newCart;
console.log(app.locals.cart);
//Returns Mongoose model
}
});
console.log(app.locals.cart);
//Now returns undefined
I solved this by storing the value to a variable in my middleware object called mids.
Here's the code on my app.js:
mids.anonymousCart();
app.use(function (req, res, next) {
res.locals.userTrue = req.user;
res.locals.cart = mids.cart;
next();
});
And here's the code on my middleware file:
var mids = {
anonymous_id: undefined,
cart: []
};
mids.anonymousCart = function () {
if (typeof mids.anonymous_id === 'undefined') {
Cart.create({}, function (err, newCart) {
if (!err) {
mids.anonymous_id = newCart._id.toString();
Cart.findById(mids.anonymous_id).populate('items').exec(function (err, cartReady) {
if (!err) {
mids.cart = cartReady;
}
});
}
});
}
}
What's happening here is that when app.js first starts, my middleware mids.anonymousCart is run. In my middleware, it checks that the id of Cart model stored as anonymous_id exists or not. It doesn't, so it creates the cart and stores the id. Since this is stored in the middleware and not Express, it can be accessed by any file linked to it.
I then turned the id I got back into a string, so that it could be read in the next function's findById. And if there's any items in the cart, Mongoose will then fill them in using the populate() method. But THE THING TO PAY ATTENTION TO is the way the data received in cartReady is stored in the middleware variable mids.cart, which is then read by res.locals.cart on app.js.

How to access parent modules when doing layered routing in mongoDB

As a personal project, I'm trying to build a social media site for teddy bear collectors. I would like users to be able to make a "collection" page which they can populate with individual profile pages for each of their bears. Finally, I would like other users to be able to comment on both the collection page and the individual profile pages.
However, I'm running into an error on the "/new" route for comments on the individual profile pages. I can't get it to find the id for the parent collection.
Below is the code I'm working with. I start by finding the id for the collection, then I get try to get the id for the bear (individual profile page). However, the process keeps getting caught at the first step.
var express = require("express");
var router = express.Router({mergeParams: true});
var Bear = require("../models/bear");
var Comment = require("../models/comment");
var Collection = require("../models/collection");
var middleware = require("../middleware");
//comments new
router.get("/new", middleware.isLoggedIn, function(req, res) {
Collection.findById(req.params.id, function(err, foundCollection) {
if(err || !foundCollection){
req.flash("error", "Collection not found");
return res.redirect("back");
}
Bear.findById(req.params.bear_id, function(err, foundBear) {
if(err){
res.redirect("back");
} else {
res.render("bcomments/new", {collection_id: req.params.id, bear: foundBear, collection: foundCollection});
}
});
});
});
//bcomments create
router.post("/", middleware.isLoggedIn, function(req, res){
Collection.findById(req.params.id, function(err, foundCollection) {
if(err || !foundCollection){
req.flash("error", "Collection not found");
return res.redirect("back");
}
//look up bear using id
Bear.findById(req.params.id, function(err, foundBear){
if(err){
console.log(err);
res.redirect("/bears" + bear._id);
} else {
//create new comment
Comment.create(req.body.comment, function(err, comment){
if(err){
req.flash("error", "Something went wrong");
console.log(err);
} else {
//add username and id to comment
comment.author.id = req.user._id;
comment.author.username = req.user.username;
//save comment
comment.save();
//connect new comment to bear
bear.comments.push(comment);
bear.save();
//redirect bear show page
req.flash("success", "Successfully added comment");
res.redirect("/collections/" + foundCollection._id + "/bears/" + foundBear._id);
}
});
}
});
});
});
So, instead of rendering the new comment form, it hits a "null" error and redirects back at the first if statement.
If anyone can help me figure this out, I'd be exceedingly grateful.
Thank you.
I think the problem is that you are defining the path "/new" (which has no parameters) and trying to access req.params.id. If you expect to have the parameter id you should define it in the path like this: router.get("/new/:id", .... Check the Express oficial documentation for more details.
EDIT:
You may have mixed req.params with req.query and req.body. If you are passing parameters in the request, you must access them through req.query (for example: req.query.id or req.query.bear_id) in the case of GET and DELETE or through req.body in POST and PUT.

Use nested variable from another file

I am working with node.js in visual studio code. Still new to both.
I am trying to call a nested variable with child properties from one js file to use in another file. How can this be done?
I have already referenced the source file in my target file and I am able to call to the file and method that contains the variable which return object.object in console.log. Any calls to the variable itself or to the object assigned to its value come back undefined. I have tried using exports.var but visual studio code does not recognize the command. The object assigned to its value does have an initial global declaration in the source file.
job and its children is the value I want to transfer over. Console.log command in source file does display the expected values.
Source File: dataHandler.js
let jobArray = null;
export function getJobDetails() {
if(_jobDetails == null) {
let job, jobIndex, regIndex;
regIndex = _productTypes.indexOf("reg");
jobIndex = (regIndex > -1) ? regIndex : 0;
job = _jobsData[jobIndex].details;
jobArray = job;
}
console.log(jobArray.orderId); //returns expected value
return _jobDetails;
}
Target File: geo.js
import * as DataHandler from './dataHandler.js';
export function createGeo() {
var site = DataHandler.jobArray.orderId;
//var site = DataHandler.getJobDetails().jobArray;
//var site = DataHandler.getJobDetails(jobArray.orderId);
Do something like following.
const DataHandler = require('./dataHandler');
var test = DataHandler.getJobDetail();
console.log(test.orderId);
& DataHandler should do something like following. Here important point is I'm returning jobarray from getJobDetails.
var jobArray = null;
var getJobDetail = () => {
jobArray = {
orderId: 1
};
return jobArray;
};
module.exports = {
getJobDetail
};
Express's app and Socket.io have nothing to do with one another.
So fundamentally, you can't use socket inside app.post.
You need to identify the client. You can use Passport which has a Socket.io plugin that essentially bridges the app.post/get's req.user to socket.request.user.
Note: It doesn't have to be an authenticated client with user that's fetched from database, just a client with a temporary user stored in memory would do. Something like this:
app.use(function(req, res, next) {
if (!req.user) { // if a user doesn't exist, create one
var id = crypto.randomBytes(10).toString('hex');
var user = { id: id };
req.logIn(user);
res.redirect(req.lastpage || '/');
return;
}
next();
});
var Users = {};
passport.serialize(function(user) {
Users[user.id] = user;
done(null, user);
});
passport.deserialize(function(id) {
var user = Users[id];
done(null, user);
});
Then you can attach the client's socket ID to its user session.
io.on('connection', function (socket) {
socket.request.user.socketid = socket.id
});
And then instead of socket.emit use io.emit in app.post using the socketid
app.post('/', function (req, res) {
io.to(req.user.socketid).emit('whatever');
});
Note: io.to() is used to emit to a room, but every socket is by default joined to the same room named as its socket.id, so it'll work.
thanks : https://stackoverflow.com/questions/28310418/nodejs-accessing-nested-variable-in-global-scope
You are not returning "jobArray", Use Promise to return the variable to your child file.

firebase : unable to user authorization data is undefined, but uid is retrieving correctly Node.js

Actually, I want to globally use the data of the user., who logged into the portal currently.
Below is the code i wrote.
I have saved the userdata to firebase database after creating the user as shown here!
firebase.auth().createUserWithEmailAndPassword(email,password).then(function(userData)
{
console.log("Successfully Created user with uid:",userData.uid);
var user ={
uid:userData.uid,
email:email,
first_name:first_name,
last_name:last_name,
location:location,
fav_genres:fav_genres,
fav_artists:fav_artists
}
var userRef =fbRef.child('users');
userRef.push().set(user);
req.flash('success_msg','you are registered now, You can login');
res.redirect('/users/login');
}).catch(function(error)
{
res.write
({
code: error.code
});
res.status(401).end();
});
}
});
Now i want to retrieve and use currenly logged in userdata globally for all the pages in my application.
some portion in app.js is below
app.get('*',function(req,res,next){
if(firebase.auth().currentUser!=null)
res.locals.user = firebase.auth().currentUser;
});
below is a node in users from firebase.
But i am not getting the user object., Able to get only uid of the current user.Please suggest me in this.
I could figure out the issue.Got it by using the orderByChild() function as shown below.
app.get('*',function(req,res,next){
if(firebase.auth().currentUser!=null){
var id = firebase.auth().currentUser.uid;
var profileRef = fbRef.child('users');
// order by uid and then find the specific uid
var query = profileRef.orderByChild('uid').equalTo(id);
// Do a one time read of that uid
query.once('value', function(snap){
var user = JSON.stringify(snap.val());
console.log('User data is: '+user);
res.locals.user = JSON.stringify(snap.val());
});
}
next();
});

Resources