I have an issue trying to associate a particular order with a particular product. I am new to node.js, I want to add a product(be associated and be populated with an order) and be redirected to a page which shows all orders of products i created
created the product and order schema(not too sure about the other schema)...
This is my product model
var mongoose= require("mongoose");
var productSchema = new mongoose.Schema({
name:String,
brand:String,
price: Number,
image: String,
description:String,
featured: Boolean,
});
module.exports= mongoose.model("Product", productSchema);
This Is order model:
var mongoose = require("mongoose");
var orderSchema= new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
products:[
{
type: mongoose.Schema.Types.ObjectId,
ref:"Product", required:true
}
]
,
quantity :{type:Number, default:1}
});
module.exports=mongoose.model("Order",orderSchema);
I want this models to be associated together using the mongoose method, be populated and then appear on a route ("/order) for example. I am new to programming and as such the simplest answer would be appreciated.
Here is a small working example using Express.Js, in this example I created 3 models :
// Models
const productSchema = new mongoose.Schema({
name:String
});
var orderSchema= new mongoose.Schema({
products:[productSchema]
});
const clientSchema = new mongoose.Schema({
name: String,
order: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Order'
}
})
const Product = mongoose.model('Product', productSchema);
const Client = mongoose.model('Client', clientSchema);
const Order = mongoose.model('Order', orderSchema);
My orderSchema will embed the products ordered by my clients, I chose to store products as subdocuments because I will have only few, you could also choose to store references instead.
Note: I'a referencing only one order per Client, you could choose otherwise, and make the order field in the clientSchema in an array (order: [{ /* type...*/}])
In my controller I have 2 methods: one to create an order and the other to add a product to the order.
// Controller
const createOrder = (userId, productId) => {
Product.findOne({_id: productId}) // find a product
.exec()
.then(product => new Order({products: [product]}).save()) // save the products into the order
.then(order => Client.findOneAndUpdate({_id: userId}, {$set: {order: order}}).exec())// update the client document with the new order
.then(client => res.json(client)); // respond with json
// .catch(err => console.log(err))
}
const addToOrder = (userId, productId) => {
return Product.findOne({_id: productId}) // find the product
.exec()
.then(product => {
return Client.findOne({_id: userId})//find the client
.populate({ path: 'order', populate: { path: 'order.products' } })
.exec() //populate in order to access client.order.products
.then(client => {
client.order.products.push(product);
return client.order.save();
})
})
// .catch(err => console.log(err));
}
I know need two routes: one to render a table with all the available products and one to render a cart with the ordered items.
// Get all products
app.get('/products', function(req, res) {
Product.find({})
.exec()
.then(products => res.render('products.ejs', {allProducts: products}))
.catch(err => res.json({errors: err}));
});
// Order a product
app.get('/products/:productId/order', function(req, res) {
req.user = {id: ""} /* id of the client, or the logged in user if you're using passeport or some other auth manager */
Client.findOne({_id: req.user.id})
.exec()
.then(client => {
if(client.order) {
return addToOrder(client.id, req.params.productId)
.then(order => res.render('cart.ejs', {order: order}))// render cart with the order
} else {
return createOrder(client.id, req.params.productId)
.then(order => res.json('cart.ejs', {order: order}))
}
})
.catch(err => res.json({errors: err}));
})
I need two (rendered by my route handlers):
// /views/products.ejs
<div>
<table>
<% allProducts.forEach(function(product) { %>
<tr>
<th>name</th>
</tr>
<tr>
<td><%= product.name %></td>
<td><a href=<%=`/products/${product.id}/order`%>> Order </a> </td>
<tr>
<% }) %>
</table>
</div>
// views/cart.ejs
<div>
<table>
<% order.products.forEach(function(product) { %>
<tr>
<th>name</th>
</tr>
<tr>
<td><%= product.name %></td>
<tr>
<% }) %>
</table>
</div>
I hope this helps.
Related
I have these collections (books,book_genres,genres,books)
my book Schema is like that
var bookModel = function () {
var bookSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
author_id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Author",
},
title: String,
description: String,
cover: String,
likes: Number,
});
return mongoose.model("Book", bookSchema);
};
module.exports = new bookModel();
I'm using dust templating and I'm rendering that collection data on my layout like that
{#books}
<div class="large-4 small-12 columns book">
<div class="row img-row">
<img src="{.cover}" alt="">
</div>
<div class="row">
<h3 class="small-12">{.title}</h3>
<h4 class="small-5 columns ">{.author_id}</h4>
<h4 class="small-7 columns ">{.name}</h4>
</div>
<div class="row p-row">
<p class="small-12">{.description}</p>
</div>
</div>
{/books}
my author Schema is like that
var authorModel = function () {
var authorSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: String,
});
return mongoose.model("Author", authorSchema);
};
I want to be able to reach to author name so I can render it on my layout through the author_id that I'm getting from the book Schema (for sure the id in the authors collection is the same as the author_id in the books collection)
I tried to search for some solutions but no one was using dust templating so I wasn't able to figure that out
You can use populate to resolve the Author reference:
bookModel.find({}).populate('author_id').exec();
You should then be able to access the referenced user fields with:
<h4 class="small-7 columns ">{.author_id.name}</h4>
In your case, you should change your code to:
module.exports = function (router) {
router.get('/', function (req, res) {
Book.find({})
.populate('author_id')
.exec(function (err, books) {
if (err) {
console.log(err);
}
var model = { books: books };
res.render('index', model);
});
});
};
My PostSchema is like this:
let PostSchema = new mongoose.Schema({
content: String,
time: Date,
likes: Number,
image: String,
tag: String
});
let Post = mongoose.model("Post", PostSchema);
module.exports = Post;
And I'd like to get a list of all the posts with the same tag
Here is my code to get it rendered. It is in the folder routes/post
router.get("/tag", function(req, res) {
Post.find({ subreddit: req.params.tag }).lean()
.then(posts => {
res.render("posts/index", { posts });
})
.catch(err => {
console.log(err);
});
});
Of course I have the views/posts folder with index.ejx to route the view
<li class="list-group-item">
<div class="text-right">
<span>Tags: </span><%= post.tag %>
</div>
</li>
I don't really understand how or where I got it wrong. I tried to follow the instruction of the tutorial here: https://www.makeschool.com/academy/track/standalone/reddit-clone-in-node-js/create-subreddits
Maybe try to trim the value you search:
router.get("/tag", function(req, res) {
let tag = req.params.tag;
if (!tag) {
throw new Error('No tag was found');
}
tag = tag.trim();
Post.find({ subreddit: req.params.tag }).lean()
.then(posts => {
res.render("posts/index", { posts });
})
.catch(err => {
console.log(err);
});
});
I am making a user management system where the admin can login and add user details into an array called data and render it in the form of a table.There is a front-end form for the same purpose.
The problem is, I am able to take information from the the front-end from and push it to the array but not able to render in that table.
This is my user schema:
const userSchema= new mongoose.Schema({
email: String,
password: String,
data: [{
name: String,
emailAdd: String,
phone: Number,
}]
});
This is the post route for receiving user info and storing it in the array.
app.post("/add_user",function(req,res){
const userId=req.body.id;
User.findOne({userId}, function(err, foundUser) {
if (err) {
console.log(err);
} else {
if (foundUser) {
foundUser.data.push({
name:req.body.fname,
emailAdd:req.body.emailadd,
phone:req.body.phone
});
foundUser.save(function() {
res.redirect("/database");
});
// console.log(foundUser);
}
}
});
})
This is the get route for passing the object to front end:
app.get("/database", function(req, res) {
const userId=req.body.id;
if(req.isAuthenticated()){
User.find({ _id:userId,"data": {$ne: null}}, function(err, foundUser) {
if (err) {
console.log(err);
} else {
res.render("database", { newUser: foundUser });
}
});
}else{
res.redirect("/");
}
});
There is a database.ejs page containing the table where I need to render it:
<table class="list" id="employeeList">
<thead>
<tr>
<th>Full Name</th>
<th>Email</th>
<th>Number</th>
</tr>
</thead>
<tbody>
<%newUser.forEach(function(user){ %>
<tr>
<td><%=user.data.name%></td>
<td><%=user.data.emailAdd%></td>
<td><%=user.data.phone%></td>
</tr>
<% }); %>
</tbody>
</table>
The table still remains empty even after adding the user.
(P.S: I am using passport for encryption and mongoDB as database)
.No errors are showing up hence I don't know where I am going wrong.
Any help will be appreciated!
I have just started NodeJS, so I'm on a beginner level. I am trying to build a shopping list app with MongoDB, in which user logs in, creates a list and then adds items to that list. I can register & log in and create a list, but when I try to add items to it, that's when I run into this:
"TypeError: Cannot read property 'products' of undefined".
Code for it is:
const user = req.user;
const shoppinglist_id = req.body.shoppinglist_id;
const name = req.body.name;
let new_product = product_model({
title: req.body.title,
imagePath: req.body.imagePath,
quantity: req.body.quantity
});
new_product.save().then(() => {
shoppinglist_id.products.push(new_product);
console.log('product saved');
shoppinglist_id.save().then(() => {
return res.redirect('/');
});
}); ```
User model:
const user_schema = new Schema({
name: {
type: String,
required: true
},
shoppinglists: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'shoppinglist',
req: true
}]
});
Shoppinglist model:
const shopping_list_schema = new Schema({
name: {
type: String,
required: true
},
products: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'product',
req: true
}]
});
Product model:
var schema = new Schema({
imagePath: {type: String, required: true},
title: {type: String, required: true},
quantity: {type: Number, required: true}
});
Any idea what I'm doing wrong? I know the problem is with this line:
"shoppinglist_id.products.push(new_product);", I have tried everything on it, "user.shoppinglists.products" etc. Nothing works. Any help?
EDIT:
I'll also post as a reference my add shoppinglist, which is working fine.
const user = req.user;
let new_shoppinglist = shopping_list_model({
name: req.body.name,
products: req.body.products
});
new_shoppinglist.save().then(() => {
console.log('shoppinglist saved');
user.shoppinglists.push(new_shoppinglist);
user.save().then(() => {
return res.redirect('/');
});
});
}```
EDIT #2:
I am adding how I am getting to shoppinglist-view:
´´´
const get_shoppinglist = (req, res, next) => {
const shoppinglist_id = req.params.id;
shopping_list_model.findOne({
_id: shoppinglist_id
}).then((shoppinglist) => {
shoppinglist.populate('products')
.execPopulate()
.then((shoppinglist) => {
let data = {
shoppinglist: shoppinglist
};
console.log(data);
let html = shoppinglist_views.shoppinglist_view(data);
res.send(html);
});
});
};´´´
And shoppinglist-view:
´´´
const shoppinglist_view = ((data) => {
let html = `
<html>
<body>
<h1></h1>
<h2>${data.shoppinglist.name}</h2>
<h4>Go back</h4>
<br>
`
data.shoppinglist.products.forEach((product) => {
html += products
html += `
<div>
<p>
<h2>Name of the shopping list: ${shoppinglists.shoppinglist.name}</h2>
<h3> Name: ${product.title}<br></h3>
<img src="${product.imagePath}" width="50px" height="50px" />
quantity: ${product.quantity} </p>
</div>
</body>
</html>
`;
});
html += `
<form action="/add-product" method="POST">
<p>Add products</p><br>
Title?<br>
<input type="text" name="title"><br>
Image-link<br>
<input type="img" name="imagePath"><br>
Quantity?<br>
<input type="number" name="quantity"><br><br>
<button type="submit">Add to list</button>
</form>
</html>
</body>`;
return html;
´´´
This is because shoppinglist_id is undefined. At the moment, you derive the value like this:
const shoppinglist_id = req.body.shoppinglist_id;
In other words, req.body has no property called shoppinglist_id. If you print the value of req.body, you will most likely see that it is a string. If you are attempting to send/receive JSON data (which I'm guessing you are) you must first parse the request body:
const data = JSON.parse(req.body);
const shoppinglist_id = data.shoppinglist_id;
Or even better:
const data = JSON.parse(req.body);
const { shoppinglist_id } = data;
Please note that you should always parse untrusted JSON inside a try ... catch block, but that's another lesson for another day.
i have this problem sorting my object of data in my index of my blog app.
I have a blog app based on Express using ejs and mongoDB using mongoose.
What i want is sorting the results so the newest post starts at the top. At this moment it will show the first post at the top.
app.js / mongoose schema
blogSchema = new mongoose.Schema({
title: String,
image: String,
body: String,
created: {type: Date, default: Date.now}
});
var Blog = mongoose.model("Blog", blogSchema);
app.js / Index route
app.get("/blogs", (req, res)=>{
Blog.find({}, (err, blogs)=>{
if(err){
console.log("Error!");
console.log(err);
} else {
res.render("index", {blogs: blogs});
}
});
});
index.ejs foreach
<% blogs.forEach(function(blog){ %>
<img alt="img" src="<%= blog.image %>">
<%= blog.title %>
<span><%= blog.created.toDateString() %></span>
<p><%- blog.body.substring(0, 200) %>...</p>
Read More
<% }) %>
Does anyone have a clue how i can do this?
You can use the sort() method of Mongoose:
Blog.find((err, blogs) => {
if (err) {
console.log(err);
} else {
res.render("index", { blogs: blogs });
}
}).sort({ created: 'desc' });