Array of users not passing to the view template - node.js

I have a route like this and works fine:
// Dashboard route
router.get('/dashboard', ensureAuthenticated, (req, res) => {
Note.find({user: req.user.id})
.then(notes => {
res.render('index/dashboard', {
notes: notes
});
});
});
on the dashboard.handlebars I can loop the array with:
{{#if notes}}
...
{{#each notes}}
{{title}}
{{body}}
{{/each}}
{{/if}}
I also have this other route, almost identical, but is not passing the array to the template:
// Admin Route
router.get('/admin', ensureAdmin, (req, res) => {
User.find({})
.then(users => {
console.log("Users count is: " + users.length)
console.log(users)
res.render('index/admin'), {
users: users
};
});
});
On the console I get both console.log working, I get the number of users and the info. So the Find() is working but the array seems like is not passed to the template.
I suspect it can be because I have a global var:
res.locals.user = req.user || null;
to keep the info of the logger user. But I changed the variables names on the Find() function and with no luck.
on the template I allways have "No users found" as result
{{#if users}}
<p>There are users</p>
{{else}}
<p>No users found</p>
{{/if}}
Any idea?

Solved, it was a ")" on bad place...
'index/admin')
was the cause
Solution is:
// Admin Route
router.get('/admin', ensureAdmin, (req, res) => {
User.find({})
.then(users => {
console.log("Users count is: " + users.length)
console.log(users)
res.render('index/admin', {
users: users
});
});
});

Related

Flash messages handlebars

How do i print out flash messages in handlebars. Like i want to print out a helper message to the user if they enter a wrong login.
Routes:
router.post('/register-user', async (req, res, next) => {
let newUser = req.body
try {
if (await userCheck.userCheck(newUser)) {
const userExists = await User.exists({ username: req.body.username })
if (userExists) {
req.session.email = req.body.username;
res.redirect('/apikey')
console.log("User exists")
} else {
try {
req.session.email = req.body.username;
await userFacade.addUser(newUser)
console.log("New user")
res.redirect('/apikey')
} catch (err) {
console.log(err)
}
}
} else {
req.flash("error", "Something went wrong during login, please try again.");
res.redirect('/')
}
} catch (error) {
console.log(error)
}
})
I have the flash message "error", but how do i show that on the page only if its set? Like if u were to use useStates in React.
I tried something like this:
{{#if error}}
{{error}}
{{/if}}
App.js
//Express session
app.use(session({
secret: process.env.SESSION_SECRET,
saveUninitialized: true,
resave: true
}));
//Flash
app.use(flash());
//Routes
app.use('/', require('./routes/index'))
In order to render the value you have saved to the flash store you would need to retrieve it from the store by calling req.flash using the same key that you used to save the value: req.flash('error'). You would then need to assign the returned value to a member of the data object you are passing to your render call in your GET handler for the / path - as this is the path you redirect your users to when you call res.redirect('/').
Your GET handle for / may become something like the following:
app.get('/', (req, res) => {
res.render('index', { error: req.flash('error') });
});
Note: The result of calling req.flash('error') is an array. Your template will work because Handlebars stringifies array values, but {{#each error}}{{this}}{{/each}} would produce the same result when error is an array with a single element. If error were to have multiple elements, the {{error}} in your template would print all elements joined with commas, as in error 1,error 2,error 3.

Cant Save to MongoDB database and using handlebars

this is my post request to save to mongdb database
router.post("/create", async (req, res) => {
const createJottings = new Jottings({
title: req.body.title,
jottings: req.body.jottings
});
try {
await createJottings.save();
res.json(createJottings);
} catch (err) {
res.json({ message: err });
}
});
it works fine on postman but now i am trying to render it using handlebars to the client. this is the form for the client side using handlebars
<div class="card card-body">
<h3>
Edit Jottings/Idea
</h3>
<form action="/jottings/create" method="get">
<div class="form-group">
<label for="title">
Title
</label>
<input type="text" name="title" class="form-control" required />
</div>
<div class="form-group">
<label for="title">
Jottings
</label>
<textarea name="Description" class="form-control" required></textarea>
</div>
<button type="submit" class="btn btn-primary">
Submit
</button>
</form>
</div>
the form actually loads but when i press submit it doesnt save to mongodb server
{{#each getJottings}}
<div class="card card-body mb-2">
<h4>
{{title}}
</h4>
<p>
{{jottings}}
</p>
<a href="/jottings/edit/{{id}}" class="btn btn-dark btn-block">
Edit
</a>
</div>
{{else}}
<p>
No Ideas and Jottings listed
</p>
{{/each}}
code that outlists saved data in the database if i create it using postman it works but with the form it doesnt.
overview of my jottings route
// Require Mongoose
const router = require("express").Router();
// Setup Models for Jotting
const Jottings = require("../models/jottings.model");
// Setting Endpoints For Routes
// Get All Jottings
router.get("/", async (req, res) => {
try {
const getJottings = await Jottings.find({}).sort({ date: "desc" });
res.render("jottings/index", {
getJottings: getJottings
});
} catch (err) {
res.json({ message: err });
}
});
// Getting routes to set form
router.get("/add", (req, res) => {
res.render("jottings/add");
});
// Get Specific Jottings
router.get("/:id", async (req, res) => {
try {
// Requesting for request paremeter given to ever document created in mongoDB
const id = req.params.id;
await Jottings.findById(id, (err, jottings) => {
if (!id) {
res.json({ message: err });
} else {
res.json(jottings);
}
});
} catch (err) {
res.json({ message: err });
}
});
// Post to create New Jottings for form
router.post("/create", async (req, res) => {
const createJottings = new Jottings({
title: req.body.title,
jottings: req.body.jottings
});
try {
await createJottings.save();
res.json(createJottings);
} catch (err) {
res.json({ message: err });
}
});
router.get("/edit/:id", async (req, res) => {
try {
// Requesting for request paremeter given to ever document created in mongoDB
const id = req.params.id;
const editJottings = await Jottings.findOne({ _id: id });
res.render("jottings/edit", {
editJottings: editJottings
});
} catch (err) {
res.json({ message: err });
}
});
// Patch to Edit Jottings for form
router.patch("/edit/:id", async (req, res) => {
try {
// Requesting for request paremeter given to ever document created in mongoDB
const id = req.params.id;
const editJottings = await Jottings.updateOne(
{ _id: id },
{ $set: { jottings: req.body.jottings } }
);
res.render("jottings/edit", {
editJottings: editJottings
});
} catch (err) {
res.json({ message: err });
}
});
// Delete to delete Jottings for form
router.delete("/delete/:id", async (req, res) => {
try {
// Requesting for request paremeter given to ever document created in mongoDB
const id = req.params.id;
const deleteJottings = await Jottings.deleteOne({ _id: id });
res.json(deleteJottings);
} catch (err) {
res.json({ message: err });
}
});
// Exporting router
module.exports = router;
i would like the details to be saved to the database when i click the submit button and also redirect me to a list of my saved details.
You form uses method GET while you defined router.post to handle form submission.

How can I tell to EJS if user is logged in?

I'm trying to check if a user is logged in to display specifc content. I have this:
app.get('/profile', isLoggedIn, (req, res) => {
res.render('profile', {
user: req.user, isLoggedIn: isLoggedIn()
});
});
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return next(null, true);
}
res.redirect('/');
}
And as template:
<%if (isLoggedIn) { %>
<div>Content 1</div>
<% } %>
But I get this error:
Cannot read property 'isAuthenticated' of undefined
What am I doing wrong?
isAuthenticated of undefined meaning req is undefined.
here you are doing mistake.
user: req.user, isLoggedIn: isLoggedIn()
You can not call middleware function like this. isLoggedIn()
What you can do is:
app.get('/profile', isLoggedIn, (req, res) => {
res.render('profile', {
user: req.user, isLoggedIn: req.isLogged
});
});
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
req.isLogged = true
return next();
}
res.redirect('/');
}
isLoggedIn function is a middleware. It doesn't returns true or false here and you should simply use req.isAuthenticated() that returns true or false if the user is authenticated or not. Here is code :-
app.get('/profile', isLoggedIn, (req, res) => {
res.render('profile', {
user: req.user, isLoggedIn:req.isAuthenticated()
});
});
A simpler way is to just look at whether the variable user is defined.
res.render('profile', {
user: req.user
});
and in your view:
<% if(typeof user != 'undefined') { %>
// do stuff
<% } %>

Mongodb and Express deleting an item from _id

I'm new to Express and databases. I'm trying to create a simple CRUD todo list by following several tutorials using Mongo DB (not Mongoose), Express, Node, and Handlebars.
I can create items from an input form and have them successfully save to the Mongo database and the page. I can't figure out how to delete each item based on a button click both on the page and from the database. Ideally, I want to target them based on the _id that Mongo creates.
I think the issue might be having something to do with the way I'm trying to select each item. Any suggestions or easier ways to implement this would be great!
Here is my code for Express:
// Connecting Mongo DB
MongoClient.connect(url, (err, client) => {
if (err) return console.log(err)
db = client.db('todoitems')
})
// Save items to DB from form
app.post('/items', (req, res) => {
db.collection('items').save(req.body, (err, result) => {
if (err) return console.log(err)
console.log(req.body, {_id: req.body._id})
res.redirect('/')
})
})
// Delete item on click from DB
app.delete('/items/:id', (req, res) => {
db.collection('items').remove({_id: req.body.id}, (err, result) => {
if (err) return console.log(err)
console.log(req.body)
res.redirect('/')
})
})
// Get items from DB to page
app.get('/', (req, res) => {
db.collection('items').find().toArray((err, result) => {
if (err) return console.log(err)
res.render('index', {
layout: false,
items: result
});
})
})
And here is my code from the Handlebars file:
<form action="/items" method="POST">
<input type="text" placeholder="item" name="item">
<input type="hidden" id="date" name="date" value="CurrentDate">
</form>
<ul>
{{#each items}}
<li>
<span>{{item}} |</span>
<span>{{date}}</span>
<button id={{_id}}>x</button>
</li>
{{/each}}
</ul>
If you are removing by _id, you need to pass an ObjectID(id) not a "string" id, something like:
remove({_id: mongodb.ObjectID( req.params.id)} ...
So, your code should be like:
app.delete('/items/:id', (req, res) => {
db.collection('items').remove({_id: mongodb.ObjectID( req.params.id)}, (err, result) => {
if (err) return console.log(err)
console.log(req.body)
res.redirect('/')
})
})

Passing form data express

Edit 2
I've tried the following.
routes
// competition form details
router.get('/dashboard/users/forms/competition-form/:id', ensureAuthenticated, (req, res) => {
CompetitionForm.find(req.params.id, function(err, competition){
res.render('dashboard/users/forms/competition-form.hbs', {
pageTitle: 'Competition Form',
users: competition
});
});
});
// competition form details post
router.post('/dashboard/users/forms/competition-form/:id', (req, res) => {
CompetitionForm.findOneAndUpdate({ _id: req.params.id }, req.body, {upsert:true}, (err, competition) => {
if (err) {
console.log(`Error saving data: ${err}`);
return res.send('Error saving data');
}
res.redirect('/dashboard');
console.log(req.body);
});
});
input
<input type="text" class="form-control" name="schoolName" placeholder="Enter school name" value="{{competition.schoolName}}"
Still no luck.
Edit 1
So I have tested your example value="{{user.schoolName}}" with users: CompetitionForm in the route, however I get the field value from the user registration and not the CompetitionForm.
Here's what I have
user model
const express = require('express');
const mongoose = require('mongoose');
var app = express();
if (app.get('env') === 'production') {
mongoose.connect(process.env.MONGODB_URI, { useMongoClient: true });
} else {
mongoose.connect('mongodb://localhost/pol-development', { useMongoClient: true });
}
var db = mongoose.connection;
mongoose.Promise = global.Promise;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
console.log("Connection has been established");
});
var CompetitionFormSchema = mongoose.Schema({
schoolName: String,
competitionDate: String,
competitionTime: String,
competitionVenue: String,
competitionTotalOfStudents: Number,
competitionTotalParticipated: Number,
competitionTotalPersonnel: Number,
competitionJudge1Name: String,
competitionJudge1Telephone: String,
competitionJudge1Email: String,
competitionJudge2Name: String,
competitionJudge2Telephone: String,
competitionJudge2Email: String,
competitionJudge3Name: String,
competitionJudge3Telephone: String,
competitionJudge3Email: String,
// admin fields
competitionRequiredPhotos: Boolean,
competitionRequiredCertifications: Boolean
});
var CompetitionForm = module.exports = mongoose.model('CompetitionForm', CompetitionFormSchema);
route
router.get('/dashboard/users/forms/competition-form/:id', ensureAuthenticated, (req, res) => {
CompetitionForm.find(req.params.id, function(err, CompetitionForm){
res.render('dashboard/users/forms/competition-form.hbs', {
pageTitle: 'Competition Form',
users: CompetitionForm
});
});
});
and for the form
<form action="/dashboard/users/forms/competition-form/{{user.id}}" method="post">
<label for="schoolName">School Name <span style="color: red">*</span></label>
<input type="text" class="form-control" name="schoolName" placeholder="Enter school name" value="{{user.schoolName}}" required>
Original
I have a competition form and a collection called competitionforms
I have the form working using the following routes
// competition form details
router.get('/dashboard/users/forms/competition-form/:id', ensureAuthenticated, (req, res) => {
CompetitionForm.find(req.params.id, function(err, CompetitionForm){
res.render('dashboard/users/forms/competition-form.hbs', {
pageTitle: 'Competition Form',
user_id: req.params.id
});
});
});
// competition form details post
router.post('/dashboard/users/forms/competition-form/:id', (req, res) => {
CompetitionForm.findOneAndUpdate({ _id: req.params.id }, req.body, {upsert:true}, (err, competition) => {
if (err) {
console.log(`Error saving data: ${err}`);
return res.send('Error saving data');
}
res.redirect('/dashboard');
console.log(req.body);
});
});
Now I want to pass the form data so the user can see what they have entered should they go back to the form.
The form is using the users id
<form action="/dashboard/users/forms/competition-form/{{user_id}}" method="post">
So I have tried the following
{{competitionforms.user.schoolName}},
{{competitionforms.users.schoolName}},
{{users.competitionforms.schoolName}} and
{{user.competitionforms.schoolName}}
I'm not sure what other combination I can try here.
It's impossible to give you specifics without knowing what your template looks like but that is probably for the better since it appears there is a general misunderstanding of how Express renders the form from the template.
A very simplistic explanation is res.render takes a template and replaces any instances of specially formatted text, such as {{foo}}, with the values specified by the variable object as the second argument. So if that variable object had a foo property, that value would be used.
Example #1:
Template (template.hbs):
Hi {{name}}! Is {{favoriteColor}} your favorite color?
Express App:
res.render('template.hbs', { name: 'John', favoriteColor: 'green' });
Rendered Output:
Hi John! Is green your favorite color?
Example #2:
The provided variable object can also include embedded objects too!
Template (template.hbs):
Hi {{name.first}}! Is {{favorites.color}} your favorite color?
Express App:
res.render('template.hbs', { name: { first: 'John' }, favorites: { color: 'green' }});
Rendered Output:
Hi John! Is green your favorite color?
Bringing it back to your example, assuming your template looks something like the following:
Template (dashboard/users/forms/competition-form.hbs):
<form action="/dashboard/users/forms/competition-form/{{user.id}}" method="post">
<label>School: <input type="text" name="schoolName" value="{{user.schoolName}}" /></label>
Then the updated Express code would look like:
router.get('/dashboard/users/forms/competition-form/:id', ensureAuthenticated, (req, res) => {
CompetitionForm.find(req.params.id, function(err, competitionForm){
res.render('dashboard/users/forms/competition-form.hbs', {
pageTitle: 'Competition Form',
user: competitionForm
});
});
});
This assumes your document object (competitionForm) has a property of schoolName.
For further reading on using Mustache templates in Express, checkout:
http://expressjs.com/en/guide/using-template-engines.html
https://github.com/janl/mustache.js
Edit:
Based on your Edit #2:
routes
// competition form details
router.get('/dashboard/users/forms/competition-form/:id', ensureAuthenticated, (req, res) => {
CompetitionForm.find(req.params.id, function(err, competition) {
res.render('dashboard/users/forms/competition-form.hbs', {
pageTitle: 'Competition Form',
users: competition
});
});
});
input
<input type="text" class="form-control" name="schoolName" placeholder="Enter school name" value="{{users.schoolName}}"
The template object properties must match the variable object properties so they can be accessed: { pageTitle: '...', users: {...} } ==> users.schoolName

Resources