I've been trying many things and couldn't write the code for the "add to cart" feature. Can you please help me in doing this?
Here is the cart model:
var cartSchema = new mongoose.Schema({
owner: {type: mongoose.Schema.Types.ObjectID, ref: 'User'},
totalPrice: {type: Number, default: 0},
items: [{
item: {type: mongoose.Schema.Types.ObjectID, ref: 'Product'},
qty: {type: Number, default: 1},
price: {type: Number, default: 0}
}]})
Here is the product model:
var productSchema = new mongoose.Schema({
category: String,
name: String,
price: Number,
image: String,
description: String,
stock: Number,
reviews: [
{
type: mongoose.Schema.Types.ObjectID, ref: 'Review'
}
]
})
Edit(detailed explanation):
step1: when I click on the add to cart button it should make a "get" request and these following things should happen:
get the id of the signed-in user i.e req.user._id
store the clicked product inside the items array in the cart model
based on the required quantity of the product it should calculate the price i.e qty*price and store it in "price" in the items array in the cart model
step2: when another product is added to the cart it should follow all the steps mentioned in step1 and in addition to that it should calculate the total price of the cart i.e. totalPrice = qty1xproduct1_price + qty2xproduct2_price + .... and store it in "totalPrice" in the cart model.
step3: when I click on view cart i.e. router.get("/cart"), this should take me to the cart and it should check for the signed-in user and show the cart which belongs to that user i.e. it should check for Cart.owner and show me all the details of the cart. Ex: (
product details in short
quantity
price= qty x price_per_product
In the end total price of all these products
)
edit 3:
Here is add to cart button:
<form action="/product/<%= product._id %>/addCart" method="POST">
<select name="quantity">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button>Add to cart</button>
</form>
Here is cart route:
router.post("/product/:id/addCart", async (req, res) => {
const quantity = req.body;
Product.findById(req.params.id, function(err, foundProduct){
if(err){
console.log(err);
}
const product = {
item: foundProduct._id,
qty: quantity,
price: foundProduct.price * quantity
}
Cart.owner = req.user._id;
Cart.itmes.push(product);
Cart.save();
res.redirect("/cart");
})
})
router.get("/cart", function(req, res){
Cart.find({owner: req.user._id}, function(err, userCart){
if(err){
console.log(err);
}
const pPrice = userCart.items.map(p => p.price);
const totalPrice = pPrice.reduce((a, b) => a + b, 0);
userCart.totalPrice = totalPrice;
userCart.save()
res.render("cart", {cart: userCart});
})
})
It shows Cannot POST /product/5f80569156202d0a624d35af/addCart
In order to help you, here's a quick example :
Simple HTML :
Quantity :
<select id="quantity">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button id="btnOK">OK</button>
In your front-end JS (I used jQuery because it's much simpler to write than plain JS) :
$("#btnOK").click( () => {
const quantity = Number( $("#quantity").val());
const productData = {
id : "123456",
quantity,
price : quantity * 20
}
$.post( "/product" , productData, result => {
console.log("Response from Node : ", result)
})
})
In your back-end JS :
router.post("/product", async (req, res) => {
const productData = req.body;
const result = await (new productModel(productData)).save();
res.send(result)
})
If you want to return the sum of the price of several items, you don't need an aggregation. You can just fetch the products and sum up their prices. For instance :
const idArray = ["1", "3", "18", "27"]
// This should return 4 products :
const products = await productModel.find({
_id: {
$in: idArray
}
})
.lean()
.exec()
const totalPrice = products
.map( p => p.price)
.reduce((a, b) => a + b, 0)
res.send(totalPrice)
Related
I have created two models in my app- one for User (_id, email, username, password) and one for Expense (_id, date, detail, amount, category). For the users, I have finished the authentication with jwt.
I want logged-in users to be able to add/remove expenses and not show their expenses to other users but I don't know how I can implement that. I am not asking for code- I would be grateful if you could roughly tell me what I need to do. Thanks!
//expense schema
const expenseSchema = new mongoose.Schema(
{
date: Date,
detail: String,
amount: Number,
category: String
}
)
//controller for adding expenses
const addExpenseController = (req, res) => {
const expense = new Expense({
"date": new Date(),
"amount": req.body.amount,
"detail": req.body.detail,
"category": "expense"
});
expense.save();
res.send('expense added');
};
You should define a ref property in the expense schema pointing at the User model (change the value of the ref attribute to equal the model name given to the users):
const expenseSchema = new mongoose.Schema(
{
...
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
}
)
Then, on creation, specify the user by setting the value of its _id.
You can either store it in the session or pass it in the body, depending on your implementation:
const addExpenseController = async (req, res) => {
try {
const expense = new Expense({
date: new Date(),
amount: req.body.amount,
detail: req.body.detail,
category: 'expense',
user: req.session.user_id, // or req.body.user_id
});
await expense.save();
res.send('expense added');
} catch (err) {
res.send('server error');
}
};
EDIT: I fixed it by adding the return-object prop to v-select
When I add a student to a database from a vuetify form, I want to be able to assign them a course. But the course has to be in a list of available courses (also in the db). I managed to do that and show all the available courses in a dropdown menu.
However, when I add the new student to the database, it sends the name of the course but not the ID of the course, so the database doesn't recognize it. I would like to link the name of the course from the v-select dropdown menu to its object ID and send the ID in the POST request.
My form component:
<v-select
:items="courses"
v-model="Courses"
item-value="name"
item-text="name"
label="Available courses"
prepend-icon="folder"
>
<template v-slot:item="{ item, attrs, on }">
<v-list-item
v-bind="attrs"
v-on="on"
>
<v-list-item-title
:id="attrs['aria-labelledby']"
v-text="item.name"
></v-list-item-title>
</v-list-item>
</template>
</v-select>
Where I store all the available courses:
computed: {
courses() {
return this.$store.state.courses;
},
The axios POST method:
methods: {
async addItem(){
const response = await axios.post("http://localhost:4000/api/student", {
name: this.name,
Courses: this.courses,
});
this.items.push(response.data);
this.name = "";
this.courses ="";
},
},
My mongoDB model:
const Student = mongoose.model(
"Student",
new mongoose.Schema({
name: String ,
Courses:
{
type: mongoose.Schema.Types.ObjectId,
ref:"id"
},
})
);
module.exports = Student;
The Course model:
const Course = mongoose.model(
"Course",
new mongoose.Schema({
name: String ,
available: {type:Boolean , default :true} ,
})
);
module.exports = Course;
Need more information on how each course object looks, and your data, but essentially, set the item-value prop to the item's object ID, and under the addItem function,
async addItem(){
const response = await axios.post("http://localhost:4000/api/student", {
id: this.courseId,
Courses: this.courses,
});
this.items.push(response.data);
this.courseId = "";
this.courses ="";
}
EDIT:
It might be a good idea to name your variables better, e.g.
// in your v-select
v-model="selectedCourse"
// in your addItem function
Course: this.selectedCourse
or
Courses: this.selectedCourses
If you just want to get id of the course in v-model of v-select, You can simply use item-value="id" instead of item-value="name".
Live Demo :
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
selectedCourse: null,
courses: [{
id: 1,
name: 'Course 1'
}, {
id: 2,
name: 'Course 2'
}, {
id: 3,
name: 'Course 3'
}, {
id: 4,
name: 'Course 4'
}, {
id: 5,
name: 'Course 5'
}],
}),
methods: {
getSelected() {
console.log(this.selectedCourse) // ID of the selected course
}
}
})
<script src="https://unpkg.com/vue#2.x/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#2.6.6/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#2.6.6/dist/vuetify.min.css"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons"/>
<div id="app">
<v-app id="inspire">
<v-container fluid>
<v-select
:items="courses"
v-model="selectedCourse"
label="Available courses"
prepend-icon="folder"
item-value="id"
item-text="name"
#change="getSelected"
></v-select>
</v-container>
</v-app>
</div>
I got an MongoDB database that consists from 3 collections.
Categories
Subcategories
Products
Each product has the following model:
const ProductSchema = new Schema({
category: String,
subcategory: String,
name: String,
description: String,
price: String,
Image: [{
url: String,
filename: String
}],
deleteImages: []
});
What I want is to query a category then get the subcategories that belong to the category and (here is the question:) from the found subcategories query the products that belong to them.
app.get("/api/front/show/:category", asyncHandler(async(req,res)=>{
const category = req.params.category;
const subcategories = await SubCategoryMd.find({'category' : category});
const products = await ProductMd.find({/* Pass here the found subcategories */});
res.json({subcategories, products});
}));
How do I query multiple objects with find?
app.get("/api/front/show/:category", asyncHandler(async(req,res)=>{
let products = []
const category = req.params.category;
const subcategories = await SubCategoryMd.find({'category' : category});
const productsBulk = await ProductMd.find({}).collation({ locale: 'el' }).sort('name');
productsBulk.forEach(product => {
subcategories.forEach(subcategory => {
if(subcategory.name === product.subcategory){
products.push(product)
}
});
});
res.json({subcategories, products});
}));
This is a simple solution but it already pulls all the products and on top of that it is a very expensive function to run in my opinion.
I'm trying to add the ID of my category documents to my budget documents. Below is the Schema for my budgets.
var {mongoose} = require('../db/mongoose');
var budgetsSchema = new mongoose.Schema({
year: Number,
categoryIDs: [{type:mongoose.Schema.ObjectId,
ref: 'categories'}]
});
var Budgets = mongoose.model('Budgets', budgetsSchema);
module.exports = {
Budgets
};
And here is the Schema for my categories collection.
var {mongoose} = require('../db/mongoose');
var categorySchema = mongoose.Schema({
name: String,
amount: Number,
sub_categories: [{
name: String,
amount: Number
}]
})
var categories = mongoose.model('categories', categorySchema);
module.exports = {
categories
};
To post categories, I use this express post request to add the categories to the category collection and its ID to the Budget collection.
//The ID is the Budget ID
app.post('/categories/:id', (req, res) => {
var id = req.params.id;
var sub_categories = req.body.sub_categories;
var category = new categories({
name: req.body.name,
amount: req.body.amount,
sub_categories
})
category.save().then((docs) => {
res.send(docs);
console.log(docs)
}).catch((e) => res.status(404).send(e));
Budgets.findById(id).then((docs) => {
if(!docs) {
res.status(404).send();
}
docs.categoryIDs.push(category._id);
}).catch((e) => {
res.send(e).status(404);
})
})
When I run this, it does add the category to the collection, but it does not add the ID to the categoryIDs array in the Budget document. Please help
First, change the model name from Plural to Singular as mentioned in the mongoose docs to avoid confusion:
The first argument is the singular name of the collection your model
is for. Mongoose automatically looks for the plural version of your
model name. Thus, for the example above, the model Tank is for the
tanks collection in the database. The .model() function makes a copy
of schema. Make sure that you've added everything you want to schema
before calling .model()!
So categories to Category and Budgets to Budget. Please verify the new before mongoose.model here, Mongoose docs ref.
var categorySchema = new mongoose.Schema({
name: String,
amount: Number,
sub_categories: [{
name: String,
amount: Number
}]
})
I cant figure out how to substract data from an EJS template. This is my Schema:
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var InventorySchema = Schema({
name: [{type: String, required: true, unique: true}],
quantity: [Number],
code: [String],
price: [Number],
stock: [{type: Boolean, default: true}]
})
var Inventory = mongoose.model("Inventory", InventorySchema);
module.exports = Inventory;
The below is the main query to show all my data:
router.get("/", (req, res)=>{
InventoryModel.find({})
.then((inventory)=>{
console.log(inventory);
res.render("inventory/index", {inventory: inventory})
})
.catch((err)=>{
console.log(err);
})
})
And below is the form Where I want to add/subtract data from:
<div class="container">
<form action="/inventory" method="POST">
<label>Name</label>
<input type="text" name="itemName" placeholder="item name"><p/>
<label>Quantity</label>
<input type="number" name="quantity" placeholder="quantity"><p/>
<button>Create</button>
</form>
</div>
So here's what I'm trying to accomplish. Whatever number I enter in the quantity field, subtract that data from my mongodb database whenever I hit the create button (which is a POST)
Any help?
Thank you
You would need to do an update on the existing document and use the $inc operator.
Example:
# create query conditions and update variables
const conditions = { },
update = { $inc: { views: 1 }}; # inc accepts negative numbers
# update documents matching condition
Model.update(conditions, update)