Get data from mongoDB collection to ejs - node.js

I was learning how to use MongoDB atlas. I connected the database with my node app and I am also able to add data to it. The only problem that I am facing is with ejs. I am not able to retrieve my filePath and title from my collection, even though I was able to log all of the data in my collection but I am stuck at how can I get title and filePath from my collection and use the data on my front-end. Here is my code:
app.js:
mongoose.connect(
"mongodb+srv://<name>:<password>t#cluster0.cqqda.mongodb.net/proDB?retryWrites=true&w=majority"
);
const connectionParams = {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
};
mongoose.set("useCreateIndex", true);
const dbName = "proDB";
const userSchema = new mongoose.Schema({
title: String,
filepath: String,
});
userSchema.plugin(findOrCreate);
const User = new mongoose.model("User", userSchema);
app.get("/", function (req, res) {
User.find({}, function (err, foundItems) {
console.log(foundItems.title);
});
res.render("index");
});
app.post("/upload", function (req, res, err) {
const user = User({
title: req.body.podcastTitle,
filepath: req.body.filePath,
});
user.save();
res.redirect("/admin-login");
});
index.ejs
<% newListItems.forEach(function(item){ %>
<div class="video-div">
<p><%=item.title%></p>
</div>
<% }) %>

You need to pass the variables you want to display to the res.render method:
res.render(view [, locals] [, callback])
Renders a view and sends the rendered HTML string to the client. Optional parameters:
locals, an object whose properties define local variables for the view.
callback, a callback function. If provided, the method returns both the possible error and rendered string, but does not perform an automated response. When an error occurs, the method invokes next(err) internally.
To make your code work, move the render function call inside the query callback, and pass the found users to it:
app.get("/", function (req, res) {
User.find({}, function (err, foundItems) {
console.log(foundItems.title);
res.render("index", {newListItems: foundItems});
});
});

Related

Empty object returned but can't execute the !posts if statement to throw an error using mongoose in express app

In a REST Api POSTS App i am testing the original callback method with async/await in express app connected via mongoose to mongo db but i recieve an empty object since the database collection is empty in order to test for a throw error. In theory using the next callback as per express documentation should shoot to my error handling middleware saved in server.js file but it doesn't instead it executes with status 200 and empty object [] returned to the client.
When the database posts collection is populated it will infact return all the documents but i want it to fire the error handler when there arn't any.
Does anyone know of a solution to this issue or recognise what i maybe during wrong ?
Heres my schema
//posts.js
const mongoose = require('mongoose');
const {Schema} = mongoose;
const postsSchema = new Schema({
title: {
type: String,
required: true
},
body: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now()
}
});
const PostModel = mongoose.model('PostModel', postsSchema, 'posts');
module.exports = PostModel;
My server.js file
//500 error handling middleware
app.use( ( err, req, res, next) => {
res.status(500);
res.sendFile(path.join(__dirname, '../public/500.html'));
console.error(err.stack);
});
Now my controller file
//posts_controller.js
const Post = require('../models/posts');
router.get('/posts/:all', async(req, res, next) => {
if(!req.params.all) {
return res.status(400).send('Missing URL parameter all');
}
await Post.find({}, function(err, posts){
if(err) {
return next(new Error('Error Thrown'));
}
if(!posts) {
return next(new Error('No posts found'));
} else {
res.status(200).send(posts);
}
return posts;
});
});
What is interesting is that promises don't work either still get empty object Any Help much appreciated!
```//posts_controller.js promised based
router.get('/posts/:all', (req, res, next) => {
if(!req.params.all) {
res.status(400).send('Missing URL parameter for all');
}
Post.find({})
.then((doc) => {
if(!doc) {
throw new Error('No ducuments found to return');
}
return doc;
})
.then((doc) => {
res.status(200).send(doc);
console.info(doc);
.catch((next));
});```
Apparently Post.find() returns an empty array in the instance where no documents are found, and an empty array is a truthy value, thus !posts is false.
You should check for the length or the array instead.
if(!posts.length)

Issue with fetching value from mongodb using mongoose it always returns [ ]

I'm trying to learn mongoose with nodejs and created a following node function as below where in I'm receiving the email ID from the Angular application and based on the EmailId trying to find the records in MongoDb:
const express = require('express');
const mongoose = require('mongoose');
var bodyParser= require("body-parser");
const port = 3000;
const app = express();
//setting up bodyParser middleWare
app.use(bodyParser.urlencoded({"extended":true}))
app.use(bodyParser.json());
//setting mongoose connection to local mongoDb
mongoose.connect("mongodb://127.0.0.1/userDetails",{useNewUrlParser:true,useUnifiedTopology:true})
const db = mongoose.connection;
var userSchema = new mongoose.Schema({
email:{type:String},
userId:{ type: String, required: true },
password:{ type: String, required: true }
});
var users = mongoose.model('User', userSchema);
//error handler middleWare
app.use(function(err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
});
app.post("/login",async(req,res,next)=>{
try {
await users.find({ email:"xyz#gmail.com"}).exec(function (err, docs) {
if (err){
console.log(err);
}
else{
console.log("First function call : ", docs);
}
})
}
catch(err){
next(err);
}
})
app.listen(port,()=>{console.log("server started")});
The above code shows no error, but the output is always First function call : [].My mongodb collection is as follows
db.User.insertMany([
{ email: "xyz#gmail.com", userId:1,password : "123#124" },
{ email: "abc#yahoo.com",userId:2,password : "123#125"},
{ email: "lmn#outlook.com", userId:3,password : "123#126"}
])
db.User.find({ email: "xyz#gmail.com"});
Where is that I'm going wrong ,what should be the approach to get the data?
Your code should find and print the data to the console assuming the email address you're looking for is actually in the collection. However, I want to point out a few things that don't quite make sense.
There is absolutely no reason to await the function call await users.find({ email:"xyz#gmail.com"}).... So you should change it to users.find({ email:"xyz#gmail.com"})
With that change, there is absolutely no reason for the router function to be async. So this is how it should look app.post("/login", (req, res, next) => {...
You're not sending a response back to the caller of /login. So when you test, it just hangs there until you cancel your request. You should add res.json({msg: 'ok'}) below console.log("First function call : ", docs);
And finally, mongoose is an awesome, promise-based library. Why not use its promise capability? It will greatly clean up your code. Here is how I would re-write your code:
app.post("/login", (req, res, next) => {
users.find({email:"xyz#gmail.com"})
.then(docs => {
console.log(docs);
res.json(docs);
})
.catch(e => {
console.log(e);
next(e);
})
})

Trouble passing mongoose variables to pug template through express

I have a User model through Mongoose, and I'm trying to create a simple CRUD API, starting with the Users part, problem is that my "index," and "create" routes seem to be working but the detail page won't seem to accept the variable being passed to it, and I can't render things on the page.
My User schema is very simple for now, just to get the API working (./models/user.js)
const userSchema = new Schema({
name: {
type: String,
maxlength: 50,
minlength: 2,
trim: true
}
})
The controllers for the routes that DO work look like this (./controllers/user_controller.js)
exports.index = function(req, res, next) {
User.find({}, (err, users) => {
res.render('userindex', {users: users})
})
}
exports.new = function(req, res, next) {
newUser = new User({ name: req.body.name })
newUser.save((err, user) => {
if (err) res.send(err)
res.redirect('/users')
})
}
exports.addUser = function(req, res, next) {
res.render('newuser')
}
The routes look like this so far (./routes/userRouter.js)
I do recognize that the routes for add and new are reversed, it'll be fixed.
userRouter.get('/', userController.index)
userRouter.get('/new', userController.addUser)
userRouter.post('/add', userController.new)
userRouter.get('/:id', userController.detail)
The only thing left is the broken controller: (./controllers/user_controller.js)
exports.detail = function(req, res, next) {
User.find({'_id': req.params.id}, (err, user) => {
if (err) res.send(err)
res.render('userdetail', {user: user})
})
}
And the template that won't seem to receive the data. (./views/userdetail.pug)
p #{user.name}
Find returns an array, so there's no user to template. Either template the first item on the collection:
res.render('userdetail', {user: user[0]})
or use .findOne instead.

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

Express making mongodb data available in route

This seems like a relatively simple issue, but I can't seem to find good documentation. I'd like to pass json data from mongodb into a route, so that it is available in my ejs template.
My schema is:
var GiveSchema = new Schema({
title: String,
shortname: String,
contents: String,
image: String,
category: String
});
module.exports = mongoose.model('GiveData', GiveSchema);
var Givedata = mongoose.model( 'GiveData' );
I'd like to pass make it available to my route below, as the variable list:
app.get('/', function(req, res) {
res.render('index.ejs',{
list: Givedata,
bootstrappedUser: req.user,
page: 'home'
});
});
You'll still need to query the database for your items.
app.get('/', function(req, res, next) {
Givedata.find(function(err, items){
if(err) { return next(err); }
res.render('index.ejs',{
list: items,
bootstrappedUser: req.user,
page: 'home'
});
});
});

Resources