How to use route parameters with Express - node.js

I'm having difficulty connecting the dots with the router.params objects with express.
Right now I'd like to have the button-success button in the code below point to a url that reads someaddress.com/formula/:item._id
HTML: (EJS Templating Engine)
<% formulas.forEach(function(item){ %>
<div class="pure-u-1 pure-u-sm-1-2 pure-u-md-1-4 pure-u-xl-1-5">
<div class="formula-block centered" id="<%= item._id %>">
<h4 class="padded-top"> <%= item.name %></h4>
<p> <%= item.description %></p>
<button class="button-success pure-button">Show</button>
<button class="button-warning pure-button">Delete</button>
</div>
</div>
<% }); %>
I am pairing that with this Express route:
router.get('/formula/:id', function(req, res){
var db = req.db;
var collection = db.get('formulas');
var id = req.params.id;
collection.find({"_id": id}, {}, function(e, doc){
res.render('formula/:id', {
formula: doc,
title: `formula for ${doc.name}`,
description: `modify and view ${doc.name} formula`
});
});
});
which then uses the information from the MongoDB document to generate the page.
It's not clear to me how you do this from looking at the documentation.
Thank you for your help.

Related

Update data in mongodb with input valors

I have three input to obtain three different values. Im using express.js , node.js, mongodb and ejs templates.
<form action="/save-profile/<%= user.id %>/<%= user.name %>/<%= user.lastname %>/<%= user.description %>" method="POST">
<div class="input-group mb-3">
<span class="input-group-text" id="basic-addon1">Name</span><%= user.username %>
<input type="text" class="form-control" placeholder="'John'" aria-label="Username" name="username">
<span class="input-group-text">lastName</span><%= user.lastname %>
<input type="text" class="form-control" placeholder="" aria-label="Server" name="lastname">
</div>
<div class="input-group">
<span class="input-group-text">Description:</span>
<textarea class="form-control" aria-label="With textarea" placeholder="" name="description"><%= user.description %></textarea>
</div>
</p><br>
<button class="btn btn-primary mb-10 btn-lg">Save</button>
</div>
</div>
In js file:
router.post('/save-profile', async (req, res) => {
const profile_id = await User.findById({ _id: req.body.id })
const updatedName = await User.findOneAndUpdate({ username: req.body.username})
const updatedlastname = await User.findOneAndUpdate({ apellido: req.body.lastname })
const updatedDescription = await User.findOneAndUpdate({ description: req.body.description })
console.log(profile_id,updatedName,updatedApellido,updatedDescription)
res.redirect('/profile')})
I tried to do it with a get but it didn't work
Firstly, action attribute in the form tag accepts the path where you are handling the form data. You only need to pass the user.id, there's no need to pass the other fields for this use-case.
<form action="/save-profile/<%= user.id %>" method="POST">
...
</form>
Secondly, in your route handler, the database record can be updated using only a single findOneAndUpdate call. You don't need to call it multiple times for every field if you're only going to update a single record.
The path written in action attribute will appear as /save-profile/1, for instance, in your route handler. Value preceding /save-profile/ i.e. 1 can be accessed by modifying the path in route handler as /save-profile/:id and in the callback you can get it by req.params.id
Finally you have this.
router.post('/save-profile/:id', async (req, res) => {
const response = await User.findOneAndUpdate(
{ _id: req.params.id },
{
username: req.body.username,
apellido: req.body.lastname,
description: req.body.description
},
{ new: true }
)
// Do something with response
res.redirect('/profile')
})

Not able to post data to database and database is not showing in Robo 3T (MongoDB/Mongoose/Express)

I'm a beginner web developer (currently in the learning phase).
So, I was trying to make my first minor backend project (a todo app) and ran into a problem with the database.
I'm using MongoDB and Mongoose as database setup and express with NodeJS as backend (with ejs as template engine).
The problem is I'm not able to post stuff to the database. It is giving error when I'm submitting the form (POST method).
And the after setting up a connection, It is not showing on Robo 3T.
I'm not sure where I'm going wrong. Please help me out with this.
The used files are attached below.
Directory structure
index.js
/** #format */
//requiring express
const express = require("express");
//setting up the port
const port = 1111;
//this module provides a way to work with directories and file paths
const path = require("path");
//requiring configuration for setting up the database to be accessed by mongoose
const db = require("./config/mongoose");
//requiring Task schema/model
//using this we will create entries and populate our collection
const Task = require("./models/task");
const { create } = require("./models/task");
//firing up express
const app = express();
app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));
app.use(express.urlencoded({ extended: true }));
app.use(express.static(__dirname + "/assets"));
let task_list = [
{
title: "College",
due_date: "2012-12-13",
category: "College"
},
{
title: "Home",
due_date: "2012-12-13",
category: "Home"
},
{
title: "Work",
due_date: "2012-12-13",
category: "Work"
},
{
title: "Group",
due_date: "2012-12-13",
category: "Group"
},
];
app.get("/", function (req, res) {
Task.find({}, function (err, task) {
if (err) {
console.log("Error occured!");
return;
}
return res.render("home", {
tasks: task
});
});
});
app.post("/new-task", function (req, res) {
console.log(req.body);
Task.create(
{
title: req.body.title,
due_date: req.body.due_date,
category: req.body.category
},
function (err, newt) {
if (err) {
console.log("Error while posting");
return;
}
console.log("Newtask created!: ", newt);
return res.redirect("back");
}
);
});
//creating a listener to the specified port
app.listen(port, function (err) {
if (err) {
console.log(`Some error occured at port: ${port}
Please try again later`);
return;
}
console.log("Yay! Server is running at # port:", port);
});
Note: I was using task_list array to check if post is working or not and it was working. But problem is occurring when I'm trying it with a persistent database (MongoDB).
mongoose.js (For connection with database)
//requiring mongoose to set up connection with database
const mongoose = require('mongoose');
//setting up connection
mongoose.connect('mongodb://localhost:27017/tasks_db', {useNewUrlParser: true, useUnifiedTopology: true});
//to check if the connection is successful or some error occured
const db = mongoose.connection;
db.on('error', console.error.bind(console, "!! Error setting up connection with database !!"));
db.once('open', function() {
console.log("Connection with database is successful!");
});
task.js (Containing the schema)
//requiring mongoose
const mongoose = require('mongoose');
//creating the schema for the document of collection
const taskSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
due_data: {
type: Date,
required: true,
get: value => value.toDateString()
},
category: {
type: String,
}
});
//compiling our schema into a model (a class for interacting with MongoDB) (an instance of model is called a document)
const Task = mongoose.model('Task', taskSchema);
module.exports = Task;
home.ejs (containing the view)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Your TO-DO List</title>
<script src="https://kit.fontawesome.com/fcba9de078.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="/css/style.css" />
</head>
<body class="toggler">
<!-- The main box containing the list and the form to add or remove item -->
<div id="todo-box-div" class="flex-row sizi">
<!-- div containing form for adding items to todo-list -->
<div id="todo-form-div" class="flex-column sizi">
<!-- div header -->
<h1>Do It!</h1>
<!-- form for adding items -->
<form action="/new-task" method="POST" id="to-do-form" class="flex-column up-down">
<input id="title" name="title" class="bottom-b font-4" type="text" placeholder="I have to do this..." required />
<!-- <textarea id="description" class="bottom-b font-4" type="text"></textarea> -->
<!-- date and category will be inline for bigger widths -->
<div id="input-inline" class="flex-row">
<input id="date" name="due_date" min="2020-07-20" class="bottom-b font-2" type="date" required />
<select id="category" name="category" class="bottom-b font-2 dropdown" type="text">
<option value="default" selected disabled>Choose a category</option>
<option value="Work">Work</option>
<option value="College">College</option>
<option value="Home">Home</option>
<option value="Group">Group</option>
</select>
</div>
<!-- button for adding item -->
<button type="submit" class="font-4" id="add"><i class="fas fa-plus"></i>Add to list</button>
</form>
</div>
<!-- div containing list items (scrollable) and button to delete items -->
<div id="todo-list-div" class="flex-column sizi">
<!-- button for deleting items -->
<button type="submit" class="font-4" id="delete"><i class="far fa-trash-alt"></i>Done</button>
<!-- list containing todo items -->
<ul>
<% for(let a of tasks) { %>
<li>
<div class="list-item">
<input type="checkbox" class="pointer" />
<div class="name-date font-3">
<p><%= a.title %></p>
<p><i class="far fa-calendar-alt"></i><%= a.due_date %></p>
</div>
<div id="redundant"></div>
<div class="categ-button font-2 disp"><%= a.category %></div>
</div>
</li>
<% } %>
</ul>
</div>
</div>
<script src="/js/script.js"></script>
</body>
</html>
Note: The form is in the todo-form-div under todo-box-div.
Example error:
Here, if you see it is printing "Error while posting" on submitting the form. The req.body is getting printed. This error message is in the error handling of app.post() method in index.js.
I was stuck for too long and decided to post it here and get some guidance. Please help me out with this.
Try the following in post method instead of Task.create() and let me know if it works
const task = new Task({title: "Work",
due_date: "2012-12-13",
category: "Work"})
task.save();

how to get an image from a folder to get to render in ejs

i am making an e-commerce website. currently, we are uploading an image using multer and rest of the data is getting saved in mongodb. all the data is getting upload correctly but when I try to get image from folder/public/image nothings work. any help would be appreciated.
this is my code for multer
app.post('/addProduct', function(req, res){
upload(req,res,(err)=>{
if(err){
res.render('addproduct',{
mesg: err
});
}
new product({
_id: new mongoose.Types.ObjectId(),
image:req.body.image,
name: req.body.name,
price: req.body.price,
description: req.body.description,
category: req.body.category,
}).save(function(err)
{
if(err){
console.log(err);
res.render('addProduct')
}else{
res.redirect('/products');
}
})
});
})
//all function for uploading images and checking file
//storage engine
const storage= multer.diskStorage({
destination: './Public/images/',
filename: function(req, file, cb){
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
//upload method
const upload = multer({
storage: storage
}).single('image');
};
This is code for getting a file from ./public/image folder
<div class= "two row">
<% products.forEach(product => { %>
<div class="column">
<img src="./Public/images/<%req.files[0].filename%>" class="cover-img">
<p><%= product.name %></p>
<h3>&dollar;<%= product.price %></h3>
<!--- <p><%= product.description %></p>-->
</div>
<% }); %>
addProduct.ejs code
<div class= "two row" onclick="">
<% products.forEach(product => { %>
<div class="column">
<img src="/images/<%req.files[0].filename%>" class="cover-img">
<p><%= product.name %></p>
<h3>&dollar;<%= product.price %></h3>
<a action="/products/productId"></a><button class="btn">Add to cart</button></a>
<!--- <p><%= product.description %></p>-->
</div>
<% }); %>
I don't think that you need to have Public prepended to the asset path. So your <img> src can just be:
<img src="/images/<%req.files[0].filename%>" class="cover-img">
However, it's important that you have static assets set up for your Express application. Follow the docs here to see how that's done.
All you really need to do for that is to have:
app.use(express.static('Public'))
somewhere at the top of your Express app.

Node.JS, Express & MongoDB Atlas:: Multiple Collections ejs

I have tried almost all the solutions but couldn't get this fixed.
I'm trying to get multiple collection of data (Mongodb Atlas) onto one ejs file. But I can't get data onto the rendered ejs sheet. But when I tested with locus I can see all the data received from the database up to res.render("/vendor/show"). But it is not passing onto ejs template.
I have three sets of data sitting on the mongo Atlas:
const vendorSchema = new mongoose.Schema({
name:String,
image:String,
imageId:String,
});
const newpromoSchema = new mongoose.Schema({
name:String,
image:String,
imageId:String,
vendor:String,
description:String,
sdate:String,
edate:String,
});
const neweventSchema = new mongoose.Schema({
name:String,
image:String,
imageId:String,
description:String,
vendor:String,
sdate:String,
edate:String,
});
router.get("/vendors/:id",function(req,res) {
var events={}; //Create Empty event Object
var promotions={}; //Create Empty promotion Object
var vendors={};//Create Empty vendor Objext
Promotion.find({},function (err,allPromotions) {
if (err) {
console.log(err);
} else {
//Find Collection And Assign It To Object
promotions=allPromotions;
}
});
Event.find({},function(err, allEvents) {
if (err) {
console.log(err);
} else {
events=allEvents;
}
});
Vendor.find({},function(err,allVendors){
if(err){
console.log(err);
}else{
vendors=allVendors;
//find order collection and passing it to ejs templates
res.render("vendors/show",{ event:events, promotion:promotions vendor:vendors});
}
});
});
Show.ejs code as
<%=vendor.name%>
<%=promotion.name%>
<%=event.name%>
You need to handle the async process. In your current code, there are three async processes. Please go through following the link you will definitely get an idea about it.
https://blog.risingstack.com/mastering-async-await-in-nodejs/
if you need more help Don’t hesitate to comment.
I managed to get all data using the below function. But now I have another issue, need to filter all the Promotions and Events by vendor But it doesn't filter.
Data Schema
const neweventSchema = new mongoose.Schema({
name:String,
image:String,
imageId:String,
description:String,
vendor:String,
sdate:String,
edate:String,
});
const Event = mongoose.model("Event",neweventSchema);
module.exports = Event;
//SCHEMA SETUP===============================================================================================================
const newpromoSchema = new mongoose.Schema({
name:String,
image:String,
imageId:String,
vendor:String,
description:String,
sdate:String,
edate:String,
});
//compile into a model
const Promotion = mongoose.model('Promotion',newpromoSchema);
module.exports = Promotion;
//SCHEMA SETUP===============================================================================================================
const vendorSchema = new mongoose.Schema({
name:String,
image:String,
imageId:String,
});
const Vendor = mongoose.model("Vendor", vendorSchema);
module.exports = Vendor;
Routes as below
router.get("/vendors/:id", function (req, res) {
Vendor.find({}, function (err, allVendors) {
if (err) {
console.log(err);
} else {
// //Find Collection And Assign It To Object
Event.find({}, function (err, allEvents) {
if (err) {
console.log(err);
} else {
Promotion.find({}, function (err, allPromotions) {
if (err) {
console.log(err);
} else {
//find order collection and passing it to ejs templates
res.render("vendors/show", {event: allEvents, promo: allPromotions, vendor: allVendors});
}
});
}
});
}
});
});
EJS Show page But It doesn't get filtered.
<!-- Trying to filter all the events by vendor if event's vendor name == vendor name then show on the show page -->
<%if(event.vendor === vendor.name){%>
<div class = "container-fluid">
<div class = "row">
<%event.forEach(function(events){%>
<div class="col-sm-6 col col-md-4 col-lg-3">
<div class="thumbnail">
<img src ="</%=events.image%>" class="rounded" width="304" height="236">
<div class="caption">
<h4><%=events.name%></h4>
<p class="font-italic font-weight-bold text-muted">
<%=events.vendor%>
</p>
<p><strong>Start Date</strong><%=events.sdate%></p>
<p><strong>End Date</strong><%=events.edate%></p>
</div>
<p>Event Details <br>
<%=events.description.substring(0,100)%>
</p>
<p>
Find More
</p>
</div>
</div>
<%})%>
</div>
</div>
<%}%>
<!-- //Trying to filter all the promotions by vendor if vendor name= to promotions's vendor name only show in the show page-->
<%if(promo.vendor===vendor.name){%>
<div class = "container-fluid">
<div class = "row">
<%promo.forEach(function(promotions){%>
<div class="col-sm-6 col col-md-4 col-lg-3">
<div class="thumbnail">
<img src ="<%=promotions.image%>" class="rounded" width="304" height="236">
<div class="caption">
<h4><%=promotions.name%></h4>
<p class="font-italic font-weight-bold text-muted"> <%=promotions.vendor%></p>
<p><strong>Start Date</strong> <%=promotions.sdate%></p>
<p><strong>End Date</strong> <%=promotions.edate%></p>
</div>
<p>
<%=promotions.description.substring(0,100)%>
</p>
<p>
Find More
</p>
</div>
</div>
<%})%>
</div>
</div>
<%}%>

Passing Variable with EJS Templating

I am using the Ejs templating engine for my expressjs project and despite passing my objects along to my view blog.ejs file, I am receiving an blogpost not defined error in my ejs file. Error is happening at my <% blogpost.forEach(function(blogpost) { %> line. I figure that is has something to do with how im passing the object and its properties, but I followed guides and it appears correct.
routes.js:
//blog
router.route('/blog')
// START POST method
.post(function(req, res) {
var blogpost = new Blogpost(); // create a new instance of a Blogpost model
blogpost.title = req.body.title; // set the blog title
blogpost.author = req.body.author; // set the author name
blogpost.content = req.body.content; // set the blog content
blogpost.date = req.body.date; // set the date of the post
//Save Blog Post
blogpost.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'Blog created.' });
});
}) // END POST method
// START GET method
.get(function(req, res) {
Blogpost.find(function(err, blogpost) {
if (err)
res.send(err);
blogpost.title = req.body.title; // update the blog title
blogpost.author = req.body.author; // set the author name
blogpost.content = req.body.content; // update the blog content
blogpost.date = req.body.date; // set the date of the post
res.render('pages/blog', {
title: blogpost.title,
author: blogpost.author,
content: blogpost.content,
date: blogpost.date
});
});
}); // END GET method
blog.ejs:
<html>
<head>
<% include ../partials/head %>
</head>
<body>
<header>
<% include ../partials/header %>
</header>
<div class="grid">
<div class="col-1-1">
<div class="body-content">
<% blogpost.forEach(function(blogpost) { %>
<h1><%= blogpost.title %></h1>
<% }); %>
</div>
</div>
</div>
<footer>
<% include ../partials/footer %>
</footer>
</body>
</html>
You're not passing an array variable called blogpost to your template, you are instead passing these variables to your template:
title: blogpost.title,
author: blogpost.author,
content: blogpost.content,
date: blogpost.date
You could just do this render() instead of the one you currently have:
res.render('pages/blog', {
blogpost: blogpost,
});

Resources