How to save data when you tried to reference the schema - javascript

I am trying to save information from a book that contains in its eschema auhtor and genre that I have it referenced in different files.
The problem is that when I make the reference in the main eschema. The book to have the reference of the author and the genre with the created book only keeps me the information of the book but does not make reference neither to the gender nor to the author
book.js
const mongoose = require('mongoose');
const bookSchema = new mongoose.Schema({
name: {
type: String,
},
author: {
type: mongoose.Schema.ObjectId,
ref: 'Author',
},
numberInStock: {
type: Number,
default: 0,
},
image: {
type: String,
default: '/path/to/default/image.png',
},
genre: {
type: mongoose.Schema.ObjectId,
ref: 'Genre',
},
});
module.exports = mongoose.model('Books', bookSchema);
author.js
const mongoose = require('mongoose');
const authorSchema = new mongoose.Schema({
name: String,
publicatons: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Book',
}],
});
module.exports = mongoose.model('Author', authorSchema);
createBook.ejs
<div class="container h-100">
<div class="row">
<div class="col-lg-6 col-md-offset-3">
<h1>Add a New Book</h1>
<form action="/books/new/create" method="post">
<div class="panel panel-default">
<div class="panel-body">
<div class="form-group">
<label for="book_name"> Book Name: </label>
<input type="text" class="form-control" name="name">
</div>
<div class="form-group">
<label for="exampleFormControlSelect1">Author</label>
<select class="form-control" name="<%= author._id %>">
<% author.forEach(function(authors){ %>
<option><%= authors._id %></option>
<% }); %>
</select>
</div>
<div class="form-group">
<label for="exampleFormControlSelect1">Genre</label>
<select class="form-control" name="<%= genre._id %>">
<% genre.forEach(function(genres){ %>
<option><%= genres.name %></option>
<% }); %>
</select>
</div>
<div class="form-group">
<label for="numberInStock">Number in stock: </label>
<input type="number" class="form-control" name="numberInStock">
</div>
<div class="form-group">
<label for="image">Image: </label>
<input type="text" class="form-control" name="image">
</div>
<button type="submit" class="btn btn-success">Success</button>
</div>
</div>
</form>
</div>
</div>
</div>
this is the part of the front where I try to send the data to the backent, but only manage to save, the name, image, quantity but not the referenced data.
controller.js
const create = (req, res, next) => {
if (!req.body) {
return res.status(400).json({
error: 'No data',
});
}
const book = new Books(req.body);
book.save((err, result) => {
if (err) {
return res.status(400).json({
error: err.message,
});
}
return res.status(201).json({
message: 'Book created succesfully',
result,
});
});
};
Routes controller
const express = require('express');
const router = express.Router();
const bookController = require('../controllers/book');
const booksave = require('../controllers/create');
const authorController = require('../controllers/author');
const genreController = require('../controllers/genre');
router.get('/books/home/:page', bookController.list);
router.get('/books/new', bookController.createTemplate);
router.post('/books/new/create', booksave.create);
router.get('/books/details/:id', bookController.bookDetail);
router.get('/books/new/create/genre', genreController.createGenreTemplate);
router.post('/books/new/genre', genreController.createGenre);
router.get('/books/new/create/author', authorController.createAuthorTemplate );
router.post('/books/new/author', authorController.createAuthor);
module.exports = router;
create new book render
controller.createTemplate = (req, res) => {
Author.find({}, (err, allAuthors) => {
if (err) {
console.log(err);
} else {
Genre.find({}, (err, allGenres) => {
if (err) {
console.log(err)
} else {
res.render('new', { author: allAuthors, genre: allGenres });
}
})
}
});
};
technically what I hope to obtain is that when I save the information of the book this has a reference to its author and to the genre and automatically the author has a references to the books that are referenced with the same

Of the values of the front I'm just getting
{name: 'Digital Fortress', numberInStock: '', image: ''}
const create = (req, res, next) => {
if (!req.body) {
return res.status(400).json({
error: 'No data received',
});
}
const book = new Books(req.body);
book.save((err, result) => {
if (err) {
return res.status(400).json({
error: err.message,
});
}
console.log(req.body);
return res.status(201).json({
message: 'Book created succesfully',
result,
});
});
};

Change the name attribute in <select class="form-control" name="<%= genre._id %>"> to name='author' same as in your bookSchema, and in <select class="form-control" name="<%= genre._id %>"> to name='genre'.
The problem is your sending a post request with the following body :
{ name: /*value*/ , <%= author._id%>: /*value*/, <%= genre._id%>: /*value*/, numberInStock: /*value*/, image: /*image*/ }
The constructor of your Book model recognizes name, numberInStock and image, but not <%=author_id%> and <%=genre._id%>.
Change this also:
author: {
type: mongoose.Schema.ObjectId,
ref: 'Author'
},
genre: {
type: mongoose.Schema.ObjectId,
ref: 'Genre'
}
to
author: {
type: mongoose.Schema.Types.ObjectId,
ref: ref: 'Author'
}
genre: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Genre'
}
You also need to pass genre._id instead of genres.name, and author._idas value attributes like this :
<form action="/books/new/create" method="post">
<select name="author">
<% author.forEach(function(authors){ %>
<option value="<%= authors._id %>" ><%= authors.name %></option>
<% }); %>
</select>
<select name="genre">
<% genre.forEach(function(genres){ %>
<option value="<%= genres._id %>" ><%= genres.name %></option>
<% }); %>
</select>
<button type="submit">Success</button>
</form>

Related

How can I fix my code so that it redirects the page according to the id of the submission in MongoDB?

I'm trying to make a simple blog app using NodeJS, MongoDB and Express. My goal here is to be able to have the user write a title and text, have it go to my MongoDB database and have it show in a new, redirected page according to the ID of the new submission. However this is not working, the page will redirect and show "Cannot GET /users/postpage/(ID here)"
Here are my file paths:
config -->
|auth.js
|keys.js
|passport.js
models -->
|Createpost.js
|User.js
routes -->
|index.js
|users.js
views -->
dashboard.ejs
postpage.ejs
app.js
My code in Createpost.js:
const TextSchema = new mongoose.Schema({
postTitle: {
type: String,
required: true
},
postText: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
const Createpost = mongoose.model('Createpost', TextSchema);
My code in users.js:
router.get('postpage/:/id', (req,res)=>{
res.send(req.params.id)
})
router.post('/postpage', async (req,res)=> {
let newPost = new Createpost({
postTitle:req.body.postTitle,
postText:req.body.postText
})
try {
newPost = await newPost.save()
res.redirect(`/users/postpage/${newPost.id}`)
} catch (error){
console.log(error)
res.render('users/postpage', {newPost:newPost})
}
})
My code in postpage.ejs:
<form action="/users/postpage" method="POST">
<div class="form-group">
<label for="postTitle">Title</label>
<input required
type="postTitle"
id="postTitle"
name="postTitle"
class="form-control"
placeholder="Enter title"
value="<%= typeof postTitle != 'undefined' ? postTitle : '' %>"
/>
</div>
<div class="form-group">
<label for="postText">text</label>
<input required
type="postText"
id="postText"
name="postText"
class="form-control"
placeholder="Enter text"
value="<%= typeof postText != 'undefined' ? postText : '' %>"
/>
</div>

Express.js with EJS application bug: the view throws a "Cannot read property 'toString'" error

I am working on a blogging application (click the link to see the GitHub repo) with Express, EJS and MongoDB.
I have Posts and Post Categories, each in its own collection.
The Categories Schema:
const mongoose = require('mongoose');
const categorySchema = new mongoose.Schema({
cat_name: {
type: String,
required: true
},
updated_at: {
type: Date,
default: Date.now()
},
created_at: {
type: Date,
default: Date.now()
}
});
module.exports = mongoose.model('Category', categorySchema);
The Posts schema:
const mongoose = require('mongoose');
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
short_description: {
type: String,
required: true
},
full_text: {
type: String,
required: true
},
category: {
type: mongoose.Schema.Types.ObjectId,
ref: 'category'
},
post_image: {
type: String,
required: false
},
updated_at: {
type: Date,
default: Date.now()
},
created_at: {
type: Date,
default: Date.now()
}
});
module.exports = mongoose.model('Post', postSchema);
There is some detail I have missed, because on updating a post, the editpost.ejs view returns a Cannot read property 'toString' of undefined error:
<form action="/dashboard/post/update/<%= post._id %>" method="POST" enctype="multipart/form-data" class="mb-0">
<div class="form-group">
<input type="text" class="form-control" name="title" value="<%= typeof form!='undefined' ? form.titleholder : post.title %>" placeholder="Title" />
</div>
<div class="form-group">
<input type="text" class="form-control" name="excerpt" value="<%= typeof form!='undefined' ? form.excerptholder : post.short_description %>" placeholder="Excerpt" />
</div>
<div class="form-group">
<textarea rows="5" class="form-control" name="body" placeholder="Full text">
<%= typeof form!='undefined' ? form.bodyholder : post.full_text %>
</textarea>
</div>
<% if (categories) { %>
<div class="form-group">
<label for="category">Choose a post category</label>
<select id="category" name="category" class="form-control">
<% categories.forEach(function(category, index) { %>
<option value="<%= category._id %>" <%=category._id.toString()==p ost.category._id.toString() ? 'selected' : ''; %>>
<%= category.cat_name %>
</option>
<% }); %>
</select>
</div>
<% } %>
<label for="postimage">Upload an image</label>
<div class="form-group">
<input type="file" name="postimage" id="postimage" size="20">
</div>
<div class="form-group d-flex mb-0">
<div class="w-50 pr-1">
<input type="submit" value="Update Post" class="btn btn-block btn-md btn-success">
</div>
<div class="w-50 pl-1">
Cancel
</div>
</div>
</form>
In the controller, the updatePost method looks like this:
exports.updatePost = (req, res, next) => {
const query = {
_id: req.params.id
}
const form = {
titleholder: req.body.title,
excerptholder: req.body.excerpt,
bodyholder: req.body.body
};
const errors = validationResult(req);
const post = {};
post._id = req.params.id;
post.title = req.body.title;
post.short_description = req.body.excerpt
post.full_text = req.body.body;
post.category = req.body.category;
if (req.file) {
post.post_image = req.file.filename;
}
if (!errors.isEmpty()) {
req.flash('danger', errors.array());
const categories = Category.find({}, (err, categories) => {
res.render('admin/editpost', {
layout: 'admin/layout',
website_name: 'MEAN Blog',
page_heading: 'Dashboard',
page_subheading: 'Edit Post',
categories: categories,
form: form,
post: post
});
});
} else {
Post.update(query, post, function(err) {
if (err) {
console.log(err);
return;
} else {
req.flash('success', "The post was successfully updated");
req.session.save(() => res.redirect('/dashboard'));
}
});
}
}
What have I missed?
I think your problem here is here:
post.category._id.toString()
When you pass category in req.body you are passing category._id as that parameter, so when you create that post object in your controller, you set post.category as req.body.category, when you return this to the view post.category is not an object with ._id property and the toString() method doesn't exist on it.
Try using post.category instead
Do nothing but handle req.file first in the post route, because you are using enctype="multipart/form-data".
Use req.file somewhere and it'll work fine! I don't know the exact reason but it works!

Node js - Push() Function Not working Correctly with reference and mongodb

I am using ObjectReferences and push the comment data to the database but it's not working Even the data is being saved but only 'id' get added, not the user and comment
Main File with Push Method
var Comment = require('./models/comments'),
Camp = require('./models/user');
app.post('/camp/:id/comment', (req,res)=>{
Camp.findById(req.params.id, (err, idf)=>{
if(err){
console.log(err);
}else{
Comment.create(req.body.comment, (err, commentz)=>{
if(err){
console.log(err);
}else{
console.log(commentz + idf);
idf.comments.push(commentz)
idf.save();
res.redirect('/camp/'+ idf._id)
}
})
}
})
})
ObjectReferences user.js
var mongoose = require('mongoose');
var schema = mongoose.Schema;
var blogSchema = new schema({
name : String,
email : String,
descr : String, // TEMPORERY
posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}
],
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
]
});
var Camp = mongoose.model("Camp", blogSchema);
module.exports= Camp;
CommentSchema comments.js
var mongoose = require('mongoose');
var schema = mongoose.Schema;
var cSchema = new schema({
user : String,
comment : String,
})
var Comment = mongoose.model("Comment", cSchema);
module.exports =Comment;
Main Form
<form action="/camp/<%=camp._id%>/comment" method="post" class="form-group">
<div class="form-group">
<input class="form-control" type="text" name="comment[user]" value="" placeholder="Name">
</div>
<div class="form-group">
<textarea class="form-control" style="height:100px" type="text" name="comment[comment]" value="" placeholder="Your Comment"></textarea>
</div>
<div class="form-group">
<button class="btn btn-primary btn-large btn-block" type="submit" name="submit"> SUBMIT</button>
</div>
</form>
The only ID added to the user's database, not all comments and in the comment, collection data added perfectly
I think instead of using commentz use commentz._id and have a callback for .save. It should work.
console.log(commentz + idf);
idf.comments.push(commentz._id)
idf.save(function(err){
if(err){
// handle error
}
res.redirect('/camp/'+ idf._id)
});

Node.js cant log Class model data from Student model

I am having an issue here. I currently am making a school directory using node.js and MongoDB. I am in an app.post request and for some reason I can't get the name of the class being linked to the student to log to the console, but createdClass.name will print...
Here is the code...
app.post("/students/:id", function(req, res){
Student.findById(req.params.id, function(err, foundStudent){
if(err){
console.log(err);
} else {
Class.create(req.body.class, function(err, createdClass){
if(err){
console.log(err);
} else {
createdClass.student.id = foundStudent._id;
createdClass.student.name = foundStudent.name;
console.log(createdClass);
createdClass.save();
foundStudent.classes.push(createdClass);
console.log(foundStudent.classes[0].name);
foundStudent.save();
}
});
}
});
res.redirect("/students/" + req.params.id);
});
Also, here are my models...
STUDENT:
var mongoose = require("mongoose");
var studentSchema = new mongoose.Schema (
{
name: String,
classes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Class"
}
],
grades: Array
}
);
module.exports = mongoose.model("Student", studentSchema);
CLASS:
var mongoose = require("mongoose");
var classSchema = new mongoose.Schema (
{
name: String,
student:
{
id:
{
type: mongoose.Schema.Types.ObjectId,
ref: "Student"
},
name: String
}
}
);
module.exports = mongoose.model("Class", classSchema);
Thank you in advance and please do let me know if there is anything I can add to make this easier to read.
Here is the page making the post request...
<div>
<h1>Student Profile</h1>
<h2>Name: <%=student.name%></h2>
<div>
<h3>Classes:
<form action="/students/<%= student._id %>" method="POST">
<%if(student.classes.length === 0){%>
<p>No classes linked to profile, please add class..</p>
<input type="text" name="class[name]" placeholder="Class name">
<% } else { %>
<% student.classes.forEach(function(course){ %>
<li><%= course.name %></li>
<% }); %>
<% } %>
</form>
</h3>
</div>
</div>
Class is a reserved word and can't be used for a variable

Find MongoDB document from id posted as hidden input

How can I find the document matching the ID being posted in a hidden input?
Here is my schema:
var MessageSchema = Schema({
name: {type: String, required: true},
message: {type: String, required: true},
replies: [{ type: Schema.Types.ObjectId, ref: 'Comment' }]
}, {timestamps: true});
Here is my form:
<% for(var i=0; i<messages.length; i++) { %>
<form action="/comment/create" method="post">
<label>Name: </label>
<input type="name" name="name">
<label>Comment: </label>
<input type="text" name="comment">
<input type="hidden" name="replyTo" value=<%= messages[i]['_id']%> >
<button>Reply</button>
</form>
<% } %>
And my post route is a mess so at this point I just want to know how to find the dang message.
app.post('/comment/create', function(req, res) {
console.log(req.body.replyTo);
var message = Message.find({ _id: req.body.replyTo });
console.log(message);
res.redirect('/');
})
console.log(req.body.replyTo) returns the id 59022ff22951ce73ed9bb773.
console.log(message) returns undefined.
The call to Messages.find is asynchronous, and therefore will not provide a proper value when assigning it's result to the variable message. You need to handle this either with a callback or a Promise:
As a callback
Message.find({ _id: req.body.replyTo }, (err, res) => {
console.log(res); // message
});
As a Promise
Message.find({ _id: req.body.replyTo })
.then((res) => {
console.log(res); // message
})
.catch((err) => {
// ...
});

Categories