Pug Express req.user Interpolation - node.js

I am having a really hard time accessing a variable in Pug from Express.
My route looks like:
router.get('/', ensureAuthenticated, function(req, res, next) {
res.render('profile/profile', {user: req.user});
});
My template looks like:
.card
.card-body
h4.card-title Local Profile
if (user)
p.card-text
strong.pr-2 ID: !{user.userEmail}
br
br
strong.pr-2 Name:
= user
br
br
strong.pr-2 Email:
= user
br
br
strong.pr-2 Password:
span.text-muted= user
a.btn.btn-default(href="/profile/edit") Edit
p.card-text
small.text-muted Last login
= user
The user object looks like:
{UID: 5, userEmail: "rtester#testing.com", userPassword: "bd4eb56b41fc3663dfe2761ff34621a544ecfe27", userLastLogin: "2017-11-20T22:18:13.000Z", userToken: "cae45ae7e68ef8024d4ad5b56c68f263"}
If I include just user without stringifying, then I get Object object. If I stringify I can output the object, but trying to access a property in the object gives me nothing.
But if I
console.log(x.userEmail)
after
var x = !{JSON.stringify(user)}
then I get the property.
Any help would be fantastic!!

use user.userEmail instead of !{user.userEmail}
this worked fine for me,
app.get('/', function(req, res, next) {
var u = {UID: 5, userEmail: "rtester#testing.com", userPassword: "bd4eb56b41fc3663dfe2761ff34621a544ecfe27", userLastLogin: "2017-11-20T22:18:13.000Z", userToken: "cae45ae7e68ef8024d4ad5b56c68f263"}
res.render('index', { user: u });
});
,
html
head
title= user.userEmail
body
h1= user.userEmail

The issue actually had nothing to do with Pug or Express. It was with the way that Passport was setting the req.user variable after finding the user using Bookshelf.js.
There were some additional nested attributes in the object. This separate answer led me to a solution to isolate the information I needed. Cannot get properties of req.user
The way to access the information in Pug is the same as laid out in the docs.

Related

passport.authorize() clearing req.user with multiple (same) strategies

I need two instances of a passport local strategy ("localA" and "localB"), one instance of this authenticates against a collection "colA" in "DbA" and is used in one route sequence (Route A), the other instance authenticates against another collection (ColB) in "DbB" and is used in a second route sequence (Route B).
In both cases, access to "req.user" is needed. In the first route, "req.user" has its expected defined value, however, in the second route, "req.user" is undefined. Here is an extract of what I believe to be the relevant code:
const userA = DbA.model(`colA`, userASchema);
passport.use(`localA`, new passportLocalStrategy({usernameField: `email`, passwordField: `password`}, userA.authenticate()));
passport.serializeUser(userA.serializeUser());
passport.deserializeUser(userA.deserializeUser());
const userB = DbB.model(`colB`, userBSchema);
passport.use(`localB`, new passportLocalStrategy({usernameField: `email`, passwordField: `password`}, userB.authenticate()));
passport.serializeUser(userB.serializeUser());
passport.deserializeUser(userB.deserializeUser());
//Route A
app.post('/routeA', passport.authenticate(`localA`), (req, res) => {
res.redirect(`/routeAA`);
});
app.get('/routeAA', function (req, res) {
res.render('routeA.ejs');
});
//Route B
app.post('/routeB', passport.authenticate(`localB`), (req, res) => {
res.redirect(`/routeBB`);
});
app.get('/routeBB', function (req, res) {
res.render('routeB.ejs');
});
It appears that this is not a new issue. Here are some related posts:
https://github.com/jaredhanson/passport/issues/803
Passport.js multiple local strategies and req.user
In post 803 user #nathan6am, states ....
I ran into the same problem, it's a bit of a hacky solution but I got
around it by using req.session?.passport?.user and deserializing the
user manually instead of using req.user in the callback.
I'm still struggling to understand how to manually de-serialize so as to force req.user to re-acquire correct values, but I did confirm that the contents of "req.session.passport.user" (for my schema) is the user's email address, so I saved that in a session variable, "req.session.email". My plan was then to write some middleware (in the next route) that would search my DB, using the contents of req.session.email, then use that DB record to extract the data that I would subsequently pass onto my rendered ejs file. It would have looked something like this:
//Route B
app.post('/routeB', passport.authenticate(`localB`), (req, res) => {
req.session.email = req.session.passport.user;
res.redirect(`/routeBB`);
});
app.get('/routeBB', hack, function (req, res) {
res.render('routeB.ejs' {key1: value1, key2: value2 });
});
function hack(req, res, next) {
// find user in DB using req.session.email
// extract need data from DB
// build object comprised of needed data
// key1: value1
// key2: value2
return next();
}
But then I realized that I have other middleware, for other routes, that rely on req.user for authorization (req.user.role=some role) ... so having req.user as undefined isn't something that can work. Is there anyone who can add some color to #nathan6am's post?
Thank you.
Tim.

Passport user handlebars template

app.use(passport.initialize());
app.use(passport.session());
app.use(function(req, res, next) {
res.locals.login = req.isAuthenticated();
res.locals.user = req.user;
console.log(res.locals.user);
next();
});
app.use('/', indexRouter);
Here above is the code i used to set my user value in the res.locals.user field.
The console gives me:
{
_id: 5fc3e49c0bfce754c8f923c9,
email: 'myemailadres#hotmail.com',
name: 'Jarne',
password: 'passwordhash......',
__v: 0
}
In my handle bars i'm now trying to use this user variable.
{{#if login}}
welkom
{{user.name}}
TESTER
{{tester}}
This doesn't work. This stays empty..
I can use {{user}} but this give me back the Json format.
How can i access the fields like name, email,... saw some examples in EJS with user.name, user._id but this doesn't seem to work in my handlebars.
Also tried to do this via the router. but same result with tester (tester.name doesn't show anything).
router.get('/', function (req, res, next) {
res.render('index', {
title: '---- Dashboard---- HOME',
tester: req.user
});
});
I also tried to set a res.locals.username = req.user.name
But this resulted in an error.
I found following solution:
{{#each user}}
{{name}}
{{_id}}
{{email}}
{{/each}}
Looks like i needed to loop through the user object.
In my case when I printed out {{user}} in navigation.hbs it showed the JSON string instead of object
This happened because I used a mongo version that requires lean() function after searching the user
add .lean function
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
}).lean()
})
hope it helps!

How to get the value on url in pug

I have an url like this: http://localhost/editblog/58a5da1df3ec9614fc9893d3
and code in pug like this:
input.form-control(type='hidden', name='id', value='')
The question is how to get the value on the url and pass it to value=''
I've known about req.params.id but it is not what could solve my issue
When you render your pug template you could send any variable as res.locals property so it will send to template:
app.get('/editblog/:id', function(req, res) {
res.render('editblog', { title: 'edit blog', id: req.params.id });
});
And now you have access to id whithin your template:
editblog.pug:
input.form-control(type='hidden', name='id', value=id)

How to embed shrinkroute url() call in jade template hrefs?

How do I translate the examples from the shrinkroute README file:
// or views...
User profile
User profile
for use in jade templates?
For example, something like
a(href="#{ url( "user", { id: 1 }) }") User profile
Thanks in advance.
First of all, ensure that you're using the shrinkroute middleware:
app.use( shrinkr.middleware );
It'll automatically provide you the following helpers:
req.buildUrl and res.locals.url - builds paths for a route. The same as using shrinkr.url().
req.buildFullUrl and res.locals.fullUrl - builds full URLs for a route. The same as using shrinkr.fullUrl().
In Jade, you simply have to use the following:
a(href=url( "user", { id: 1 } )) My Username
a(href=fullUrl( "user", { id: 1 } )) My Username
Rendered output:
My Username
My Username
The above output will depend on the routes you have named in your shrinkroute instance.
Disclaimer: I'm the creator of Shrinkroute.
here's a general solution for calling a function from within a template; see #gustavohenke 's answer for a specific solution for how to use shrinkroute's built-in locals.buildFullUrl function within a jade template
// node.js
var url = require('url');
// Set up locals.shrinkUrl for every request
app.all('*', function(req, res, next){
res.locals.shrinkUrl = function(path, queryObject){
var out = url.format({
pathname: path,
query: queryObject,
});
return out;
};
next();
});
// template.jade
a(href=locals.shrinkUrl("user", {id: 1}) ) User profile
// rendered
<a href='/user?id=1'>User profile</a>

Can't access data from a POST call by express saved by Mongoose

I am just learning Node, Mongoose and Express. I've looked all through stackoverflow and else where, and I still cannot find an answer to my question.
I'm fairly certain this a just a very beginner mistake. When I save posted data inside of app.post, I cannot access it anywhere else in my node/express code. I get the error "ReferenceError: getUsersInfo is not defined."
var mongoose = require('mongoose');
mongoose = mongoose.createConnection('localhost', '27017');
mongoose.on('error', console.error.bind(console, 'connection error:'));
var Schema = mongoose.Schema
, ObjectId = Schema.ObjectID;
var usersSchema = new Schema({
username: String,
password: String,
email: String,
date_created:{ type: Date, default: Date.now }
});
var users = mongoose.model('users', usersSchema);
app.get('/', function(req,res){
res.render('layout.jade', {
title : "Hello!"
,analytics: 'XXXXXX'
});
});
app.post('/', function(req, res){
var getUsersInfo = new users({
username: req.body.user,
password: req.body.password,
email: req.body.email,
});
getUsersInfo.save(function (err, getUsersInfo) {
if (err){throw err;}
console.log(getUsersInfo.username);
res.redirect("/success");
});
});
app.get('/success', function(req, res){
res.render('loggedin.jade', {
title : "Admin Panel",
analytics: 'XXXXXX'
});
var username = getUsersInfo("username");
res.write("<h1> hi, " + username + "</h1>");
});
Any help you can give me such as places to look or a better way to write my code would be much appreciated. I tried learning from out-dated tutorials and haven't gotten anywhere and the mongoose api/docs don't seem to cover this thing.
Thanks!
EDIT
To clarify what I'm trying to achieve here is I want to take form inputs (rendered in the layout.jade file) and save them to the database (Which I'm doing in app.post). I want to then be able to access those database objects elsewhere in the code, not specifically in just app.get('/success'). But I keep running into a scope issue, it seems.
In a nodejs/expressjs application, your routes (app.get, app.post etc) are set up with callback functions that will be invoked when a user requests the corresponding URL. All operations that should be done in a request needs to be invoked from within these callbacks. In your example, you're attempting to access getUsersInfo outside the callback you have set up in app.post. If you move the code inside the callback instead, it should get you further:
app.post('/', function(req, res) {
var getUsersInfo = new users({
username: req.body.user,
password: req.body.password,
email: req.body-email
});
getUsersInfo.save(function(err, userinfo) {
if(!err) {
res.redirect('/success');
}
else {
// Send error
}
});
});
Edit: Answer above was confused by the indentation of the code, which should be corrected now. It looks like you're making a regular web app, so I would recommend that you check out the session middleware. An easy way to get started with it is to generate your express app with the command express --sessions myapplication. This will set your application so you have a req.session object where you can put session scoped objects:
app.post('/', function(req, res) {
var getUsersInfo = new users({
username: req.body.user,
password: req.body.password,
email: req.body-email
});
getUsersInfo.save(function(err, userinfo) {
if(!err) {
req.session.user = userInfo;
res.redirect('/success');
}
else {
// Send error
}
});
});
Then in your app.get
app.get('/success', function(req, res) {
var user = req.session.user;
// Do what you want with the user
});
If you were using a REST approach, a typical pattern when saving and then retrieving a resource is to redirect with GET to the URL fetching the created resource, and include an identifier for the resource. In that case, your redirect would look like this
res.redirect('/success/' + userInfo.id)
And you could make a route like this:
app.get('/success/:userId', function(req, res) {
var user_id = req.param('userId');
users.findById(user_id, function(err, user) {
// user is available here. Add it to the template context and render it.
});
});
The syntax :userId in the uri indicates a path variable named userId which will be available in req.params.
But if you're writing a regular web application, I would recommend the first approach using the session middleware.

Resources