How to add values into JSON-array (node.js/mongoose) - javascript

I'm using mongoose, adding values from HTML and saving to db with help of mongoose. I'm having issue adding value from req.body.chapter into array from HTML.
Route:
const newBook = {
book: req.body.book,
summary: req.body.summary,
chapters: //how to add value into chapter-array?
}
Book-model:
const BookSchema = new Schema({
Title: {
type: String,
required: true
},
Summary: {
type: String,
required: true
},
chapters : [{
chapter: {
type: String,
required: true
}]
});
HTML:
<div class="form-group">
<label for="book">Title:</label>
<input type="text" class="form-control" name="book" required>
</div>
<div class="form-group">
<label for="Summary">Summary:</label>
<input type="text" class="form-control" name="Summary" required>
</div>
<div class="form-group">
<label for="chapter">chapter:</label>
<input type="text" class="form-control" name="chapter" required>
</div>

So if the data looks like this, you can just put it in:
req.body.chapters = ["1", "3"];
Route:
const newBook = {
book: req.body.book,
summary: req.body.summary,
chapters: req.body.chapters
}
If you just want to add the chapters to existing data, then have a look at Using Mongoose / MongoDB $addToSet functionality on array of objects

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!

"Cast to Number failed for value 1,1 at path"

I'm trying to build a user model with some privileges.
Schema looks like this:
var mongoose = require("mongoose"),
passportLocalMongoose = require("passport-local-mongoose");
let userSchema = new mongoose.Schema({
username:
{type: String,
unique: true
},
password: String,
privileges:
[{
region: { type: Number, unique: true },
read: Number,
write: Number,
edit: Number
}]
});
userSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", userSchema);
POST route looks like this.
router.post('/register', function(req, res)
{
console.log(req.body);
User.register(new User({
username: req.body.username,
privileges:{
region: req.body.privileges['region'],
read: req.body.privileges['read'],
write: req.body.privileges['write'],
edit: req.body.privileges['edit']
}
}), req.body.password, function(err)
{
if(err)
{
console.log(err);
res.redirect("/register");
}
else
{
console.log("fine");
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login'
})
}
})
});
<form action="/register" method="POST">
<input type="text" name="username"><br/>
<input type="password" name="password"><br/>
<input type="text" name="privileges[region]"><br/>
<input type="text" name="privileges[read]"><br/>
<input type="text" name="privileges[write]"><br/>
<input type="text" name="privileges[edit]"><br/>
<input type="text" name="privileges[delete]"><br/>
<button>Submit</button>
</form>
Basically, it should work like this:
From the form I should get an array of privileges.
Now when I enter data in the fields like this:
test
1234
1 2
1 1
1 1
1 1
(test - username, 1234 - password, 1 2 region array, 1 1 - read array, 1 1 write array, 1 1 edit array) I get this error:
Now I get the reason - privileges[edit] is type="text" and it can't be parsed into the DB as a Number. But why does it happen ONLY for edit? I find it strange.
I tried changing input type to number, but after that I can't enter an array anymore.
I think I might need a middleware which transforms the text into numbers. Am I right? If so, how should it do it? Should it transform each element of the array individually or the array as a whole?
Thanks.
In the schema defined for User, privileges is an array of SubDocuments having this schema.
{
region: { type: Number, unique: true },
read: Number,
write: Number,
edit: Number
}
When setting this field, the data provided needs to match that schema. e.g.
new User({
username: req.body.username,
privileges: [
{
region: ":region_value",
read: ":read_value",
write: ":write_value",
edit: ":edit_value"
},
{
region: ":region_value",
read: ":read_value",
write: ":write_value",
edit: ":edit_value"
},
//....
],
})
I assume that the design for privileges was done purposely in this way to allow for a user to have many privileges.
A straightforward way to set privileges is to design the form appropriately. The form field can allow for setting several privileges. For example, to set two privileges, you can achieve that by writing the markup this way:
<input type="text" name="privileges[0][region]"><br/>
<input type="text" name="privileges[0][read]"><br/>
<input type="text" name="privileges[0][write]"><br/>
<input type="text" name="privileges[0][edit]"><br/>
<input type="text" name="privileges[0][delete]"><br/>
<input type="text" name="privileges[1][region]"><br/>
<input type="text" name="privileges[1][read]"><br/>
<input type="text" name="privileges[1][write]"><br/>
<input type="text" name="privileges[1][edit]"><br/>
<input type="text" name="privileges[1][delete]"><br/>
This way privileges in the req.body will have the right format e.g.
{ privileges:
[ { region: '1', read: '2', write: '2', edit: '2', delete: '4' },
{ region: '2', read: '4', write: '4', edit: '4', delete: '4' } ] }
So that you can simply write
new User({
username: req.body.username,
privileges: req.body.privileges
})
It's more straightforward to ensure the client passes the right data than trying to massage the data after the fact.
A limit in the design of the form this way means that the number of privileges that a user can have has to be determined ahead of time. A work around this is to build the form dynamically and give control to the user to add more privileges as the case may be. See the following example to get an idea about how to go about it:
function addPrivilege(e) {
e.preventDefault();
const privileges = $('.privileges');
const lastCount = privileges.length;
console.log($(this).data('template').replace(/:x:/g, lastCount))
const template = $(this).data('template').replace(':x:', lastCount);
privileges.after($('<div class="privileges"></div>').append(template))
}
$(document).ready(function () {
$("#addPrivilegeBtn").on('click', addPrivilege);
});
.privileges {
background: #ccc;
padding: 8px 16px;
margin: 4px 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
<div class="privileges">
<input type="text" name="privileges[0][region]"><br/>
<input type="text" name="privileges[0][read]"><br/>
<input type="text" name="privileges[0][write]"><br/>
<input type="text" name="privileges[0][edit]"><br/>
<input type="text" name="privileges[0][delete]"><br/>
</div>
<button
id="addPrivilegeBtn"
data-template='<input type="text"name="privileges[:x:][region]"><br/>
<input type="text" name="privileges[:x:][read]"><br/>
<input type="text" name="privileges[:x:][write]"><br/>
<input type="text" name="privileges[:x:][edit]"><br/>
<input type="text" name="privileges[:x:][delete]"><br/>'
>Add privilege</button>
</form>

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)
});

Angular - Mongoose : Can't manipulate dates

I have been searching for a long while for solution, but nothing helped me.
I have an Angular JS app which runs with Mongoose and Express.
I want to store date as object from a simple form.
But when I submit my form, dates are stored as String and not as Objects.
So I can't do something like :
tache.start.getDate();
Here is my form :
<form name="formTache" ng-submit="addTache(tache)">
<div class="form-group row">
<div class="col-lg-12">
<input name="name" class="form-control" placeholder="Nom" type="text" ng-model="tache.title"/>
</div>
</div>
<div class="form-group row">
<div class="col-lg-12">
<input id="date" name="date" class="form-control" placeholder="Date" ng-model="tache.start" type="date"/>
</div>
</div>
<div class="form-group row">
<div class="text-right col-lg-12">
<button type="submit" class="btn btn-default">Ajouter</button>
</div>
</div>
Here is my Mongoose Schema :
var restful = require('node-restful');
var mongoose = restful.mongoose;
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var tachesSchema = new mongoose.Schema({
title : String,
start: {type: Date, default: new Date()},
});
var tachesModel = mongoose.model('taches', tachesSchema);
module.exports = restful.model('taches', tachesSchema);
Here is my Controller :
angular.module('personaldashboard.tache', [])
.controller('TachesCtrl', function($scope, Taches, Progress, toaster) {
$scope.tache = new Taches();
var refreshTache = function() {
$scope.taches = Taches.query();
$scope.tache = ''
}
refreshTache();
$scope.addTache = function(tache) {
Taches.save(tache,function(tache){
refreshTache();
});
};
$scope.updateTache = function(tache) {
tache.$update(function(){
refreshTache();
});
};
$scope.removeTache = function(tache) {
tache.$delete(function(){
refreshTache();
});
};
$scope.editTache = function(id) {
$scope.tache = Taches.get({ id: id });
};
$scope.deselectTache = function() {
$scope.tache = ''
}
$scope.editTache_Projet = function(tache, projetId) {
tache.projet = projetId;
tache.$update(function(){
refreshTache();
});
};
});
Here is what I get :
{ "_id": "58a99df24975ad0104c692b1", "title": "Test", "start": "2017-02-24T23:00:00.000Z" }
So why do I get a string like "2017-02-24T23:00:00.000Z" for my date instead of an object whereas my Mongoose schema specify start: {type: Date, default: new Date()} ?
Thanks for your help.
EDIT
Thanks to Saurabh Agrawal, I tried to convert the date when submiting in the controller :
$scope.addTache = function(tache) {
tache.start = new Date(tache.start);
tache.end = new Date(tache.end);
Taches.save(tache,function(tache){
refreshTache();
});
};
Sadly, this changed nothing :(
I still have a date as string
"2017-02-20T23:00:00.000Z"
EDIT
I also tried to add a directive
.directive("formatDate", function(){
return {
scope: {ngModel:'='},
link: function(scope) {
if (scope.ngModel) {
scope.ngModel = new Date(scope.ngModel);
}
}
}
})
and call it in my form
<form name="formTache" ng-submit="addTache(tache)">
<div class="form-group row">
<div class="col-lg-12">
<input name="name" class="form-control" placeholder="Nom" type="text" ng-model="tache.title"/>
</div>
</div>
<div class="form-group row">
<div class="col-lg-12">
<input id="date" name="date" class="form-control" placeholder="Date" ng-model="tache.start" type="date" formatDate/>
</div>
</div>
<div class="form-group row">
<div class="text-right col-lg-12">
<button type="submit" class="btn btn-default">Ajouter</button>
</div>
</div>
But nothing changes.
Any other ideas ?
By searching, I 've found that I was missing the real problem.
My dates are date objects.
When i do this
$scope.test = new Date([2017,2,15]);
<pre>{{test}}</pre>
<pre>{{test.getDate()}}</pre>
I get
"2017-02-14T23:00:00.000Z" and 15
So date displaying like "2017-02-14T23:00:00.000Z" are objects.
But in my case, when i try to do the same with a date whitch is in another object like in this schema :
var tachesSchema = new mongoose.Schema({
title : String,
start: {type: Date, default: new Date()},
end: {type: Date, default: new Date()},
comment : String,
state : Boolean,
projet : { type: ObjectId, ref: 'Projets' }
});
I get nothing :(
This code :
{{tache.start}}
displays the date like this "2017-02-20T23:00:00.000Z"
but
<pre>{{tache.start.getDate()}}</pre>
displays nothing.
What I missed ?

Categories