I am trying to create parent-child like nested system where child has same schema as of parent.Below is my parent schema, here children refers to the parentSchema again,
var mongoose = require("mongoose");
var parentSchema = new mongoose.Schema({
name: String,
children:[
{
ref: this
}
],
});
module.exports = mongoose.model("Parent", parentSchema);
Routes looks like this
app.get("/", function(req, res){
Parent.find({}).populate("children").exec(function(err, allParents){
if(err){
console.log(err);
}else{
res.render("index",{parents: allParents});
}
});
});
app.post("/", function(req,res){
var name = req.body.name;
var desc = req.body.desc;
var newParent = {name: name, description: desc}
Parent.create(newParent, function(err, newlyCreate){
if(err){
console.log(err);
}else{
res.redirect("/");
}
});
});
app.post("/:id", function(req, res){
Parent.findById(req.params.id, function(err, parent){
if(err){
console.log(err);
res.redirect("/");
}else{
parent.children.push(req.body.child);
parent.save();
console.log(parent.children);
res.redirect("/");
}
});
});
The problem is when I send data from form to post route it prints it but after pushing it into parent.children and then printing parent.children is shows null. Where is the problem???
EJS page looks like below:-
<form action="/" method="POST">
<div class="form-group">
<input type="text" name="name" placeholder="Name">
</div>
<div class="form-group">
<input type="text" name="desc" placeholder="Description">
</div>
<div class="form-group">
<button class=btn-primary btn-block">Submit</button>
Go Back
</div>
</form>
<div class="row">
<% parents.forEach(function(module){%>
<ul class="list-group">
<li class="list-group-item" style="margin-bottom: 5%;">
<h2><%= module.name%></h2>
<%= module.description %>
<% console.log(module.children) %>
<%module.children.forEach(function(node){%>
<% console.log(node) %>
<%});%>
<div class="container">
<div class="row">
<div>
<form action="/<%= module._id %>" method="POST">
<div class="form-group">
<input type="text" name="name" placeholder="Name">
</div>
<div class="form-group">
<input type="text" name="desc" placeholder="Description">
</div>
<button>Submit</button>
</form>
</div>
</div>
</div>
</li>
</ul>
</div>
<% }); %>
</div>
Can anyone tell where is the problem in above code or can anyone suggest any other way to make this type of parent-child structure????
It seems that parent.save() is asynchronus. Maybe you can try this instead.
parent.save().then(()=>{
console.log(parent.children);
res.redirect("/");
});
or you can use async-await after putting async before function definition,
await parent.save();
console.log(parent.children);
res.redirect("/");
Please do write in comment if problem still persists.
Related
I got this error
CastError: Cast to ObjectId failed for value "details5f9c3b069dc8723528c64cc8" at path "_id" for model "Book"
the edit and delete button are not activated.
Help me to figure it out please. this is /routes/books.js
and what else do I upload here?
// GET edit an existing Book
router.get('/:id', (req, res, next) => {
let id = req.params.id;
book.findById(id, (err, bookToEdit) => {
if(err) {
console.log(err);
//res.end(err);
} else {
//show the edit view
res.render('books/details', {title: 'Edit Book', books: bookToEdit})
}
});
});
// POST and update the document
router.post('/:id', (req, res, next) => {
let id = req.params.id
let updatedBook = book({
_id: id,
title: req.body.title,
description: req.body.description,
price: req.body.price,
author: req.body.author,
genre: req.body.genre
});
book.updateOne({_id: id}, updatedBook, (err) => {
if(err) {
console.log(err);
res.end(err);
} else {
// refresh
res.redirect('/books');
}
});
});
// GET - process the delete by user id
router.get('/delete/:id', (req, res, next) => {
let id = req.params.id;
book.remove({_id: id}, (err) => {
if(err) {
console.log(err);
res.end(err);
} else {
// refresh
res.redirect('/books');
}
});
});
This is /books/details.ejs
<div class="container">
<div class="row">
<form class="form" method="post">
<div class="form-group">
<label for="TitleTextField">Title</label>
<input type="text" class="form-control" id="TitleTextField"
placeholder="Book Title" name="title" value="<%= books.Title %>" required>
</div>
<div class="form-group">
<label for="AuthorTextField">Author</label>
<input type="text" class="form-control" id="AuthorTextField"
placeholder="Book Author" name="author" value="<%= books.Author %>" required>
</div>
<div class="form-group">
<label for="PriceTextField">Price</label>
<input type="text" class="form-control" id="PriceTextField"
placeholder="Book Price" name="price" value="<%= books.Price %>" required>
</div>
</div>
</div>
and this is /books/indexejs
<% include ../partials/header.ejs %>
<!-- MAIN CONTENT -->
<div class="container">
<div class="row">
<!--Add-->
<i class="fa fa-plus"></i> Add a book
<br>
<br>
<div class="table-responsive">
<table class="table table-bordered table-striped table-hover">
<thead>
<tr>
<th>Title</th>
<th class="text-center">Author</th>
<th class ="col-4">Price</th>
<th class="text-center">EDIT</th>
<th class="text-center">DELETE</th>
</tr>
</thead>
<tbody>
<% for (let count = 0; count < books.length; count++) { %>
<tr>
<td><%= books[count].Title %></td>
<td class="text-center"><%= books[count].Author %></td>
<td class="text-center">$<%= books[count].Price %></td>
<!--EDIT-->
</td>
<td class="text-center">
<a href="/books/edit<%= books[count].id %>" class="btn btn-primary btn-sm">
<i class="fas fa-pencil-alt"></i> Edit</a>
</td>
<!--DELETE-->
<td class="text-center">
<a href="/books/delete<%= books[count].id %>" class="btn btn-danger btn-sm">
<i class="fas fa-trash-alt"></i> Delete</a></td>
</td>
</tr>
<% } %>
</tbody>
</table>
</div>
</div>
</div>
</div>
<% include ../partials/footer.ejs %>
this is /models/books.js
let mongoose = require('mongoose');
let Book = mongoose.Schema({
Title: String,
Description: String,
Price: Number,
Author: String,
Genre: String
},
{
collection: "books"
});
module.exports = mongoose.model('Book', Book);
hope these are helpful to fix the problems
You have an error that said that book.find and book.delete need an ObjectId as parameter. And details5f9c3b069dc8723528c64cc8 is not a valid ObjectId for Mongo.
Just read the error message !
Edition
Maybe you face this problem because you made a little mistake with resources, using same http POST to create and edit, giving the :id in path in both case.
If you have a POST / (no id in path) method to create books, letting mongodb generate ids, and have a PATCH (or PUT) /:id method to update those books, you probably will not have this kind of error anymore.
It will maybe sounds like stupid duplication but as your app will grow, you certainly will need to add some specific security rules.
Also, if I remember mongoose correctly, you're not supposed update the _id in second parameter for updateOne.
//no _id present in updatedBook
book.updateOne({_id: id}, updatedBook, (err) => { };
The default key of a document in MongoDB is _id who set an ObjectId in it.
ObjectId is a unique key who contained from some meaningful base on date and time (you can convert objectId to date).
You can read more about it here.
And your problem, it's really easy. You concat your ObjectId with details as you can see in the error.
details5f9c3b069dc8723528c64cc8 => 5f9c3b069dc8723528c64cc8
I'm relatively new to node and express, and have come across a problem I can't seem to solve.
So I came across with the problem trying to validate a create product form, that when I introduce correct values into the fields express-validator throws error.
This is my routes code:
router.post('/create', [
check('name').isLength({ min: 5 }).withMessage("Invalid Value"),
check('description').isLength({ min: 20 }).withMessage("Invalid Value")
] , upload.any() , productosController.guardarCreado);
This is my productosController code:
guardarCreado: (req, res, next) => {
let errors = validationResult(req);
if(errors.isEmpty()){
db.Product.create({
name: req.body.nombre,
description: req.body.descripcion,
size: req.body.talle,
price: req.body.precio,
category_id: 2,
img_url: req.files[0].filename
})
res.redirect('/productos');
}else{
db.Category.findAll()
.then(function(categorias){
return res.render('crearProductos', { categorias: categorias, errors: errors.errors });
})
}
}
And my form code is from Bootstrap and the names of the form fields are the same as the names on the 'check' from my routes code:
<div class="col-md-3 mb-3">
<label for="validationDefault03">Nombre del Producto</label>
<input name="name" id="nombre" type="text" class="form-control" placeholder="Ej: Remera River Plate" >
</div>
<div class="col-12">
<label for="description" class="form-label">DescipciĆ³n:</label><br>
<textarea name="description" id="description" class="form-input"></textarea>
</div>
Error display on the the form via EJS:
<% if (typeof errors != 'undefined') { %>
<p style= "color: red">ERROR</p>
</div>
<ul>
<% for(let i = 0; i < errors.length; i++) { %>
<li>
<%= errors[i].msg %>
</li>
<% } %>
</ul>
<% } %>
I tried to edit the info and update it, but the mongoose findByIdAndUpdate didn't work. The {new: true} is included, but it still shows the original one. In the database, the info is not updated either. The console shows PUT /blogs/:id 302.
The routes are as follows:
//edit
router.get('/blogs/:id/edit', function (req, res) {
Blog.findById(req.params.id, function (err, blogFound) {
if(err){
console.log(err);
}else{
res.render("edit", {blog: blogFound});
}
});
});
//update
router.put('/blogs/:id', function (req, res) {
Blog.findByIdAndUpdate(req.params.id, req.body.blog, {new: true},function (err, blogUpdated) {
if(err){
console.log(err);
} else{
res.redirect('/blogs/' + req.params.id);
}
});
});
The edit form looks like this:
<form class="ui form" action="/blogs/<%= blog._id %>?_method=PUT" method="POST">
<div class="field">
<label>Title</label>
<input type="text" name="title" value="<%= blog.title %>">
</div>
<div class="field">
<label>Image</label>
<input type="text" name="image" value="<%= blog.image %>">
</div>
<div class="field">
<label>Content</label>
<textarea name="description"><%= blog.description %></textarea>
</div>
<button class="ui primary button" type="submit">Submit</button>
</form>
it seems req.body.blog is undefined.
you have title, image and description in your body not grouped in blog object, so you shall write
const { title, image, description } = req.body;
Blog.findByIdAndUpdate(req.params.id, { title, image, description },
or smth like this
I'm attempting to build something similar to Colt Steele's YelpCamp project from his web Dev Bootcamp on Udemy. My project worked up until a refactor toward the end of the course using provided YouTube material. I get the following error message attempting to display anything from the database:
SyntaxError: Unexpected token || in /var/www/html/views/components/show.ejs while compiling ejs
If the above error is not helpful, you may want to try EJS-Lint:
https://github.com/RyanZim/EJS-Lint
Or, if you meant to create an async function, pass `async: true` as an option.
at new Function (<anonymous>)
at Template.compile (/var/www/html/node_modules/ejs/lib/ejs.js:626:12)
at Object.compile (/var/www/html/node_modules/ejs/lib/ejs.js:366:16)
at handleCache (/var/www/html/node_modules/ejs/lib/ejs.js:215:18)
at tryHandleCache (/var/www/html/node_modules/ejs/lib/ejs.js:254:16)
at View.exports.renderFile [as engine] (/var/www/html/node_modules/ejs/lib/ejs.js:459:10)
at View.render (/var/www/html/node_modules/express/lib/view.js:135:8)
at tryRender (/var/www/html/node_modules/express/lib/application.js:640:10)
at Function.render (/var/www/html/node_modules/express/lib/application.js:592:3)
at ServerResponse.render (/var/www/html/node_modules/express/lib/response.js:1012:7)
at /var/www/html/routes/components.js:84:17
at /var/www/html/node_modules/mongoose/lib/model.js:4834:16
at /var/www/html/node_modules/mongoose/lib/helpers/promiseOrCallback.js:24:16
at /var/www/html/node_modules/mongoose/lib/model.js:4857:21
at _hooks.execPost (/var/www/html/node_modules/mongoose/lib/query.js:4366:11)
at /var/www/html/node_modules/kareem/index.js:135:16
My /components/show (essentially campgrounds/show) code looks like this:
<%- include ("../partials/header") %>
<title><%=component.name%></title>
<%- include ("../partials/nav") %>
<div class="row">
<div class="col-md-3">
<p class="lead">PC Part Inventory</p>
<div class="list-group">
<li class="list-group-item active">Info 1</li>
<li class="list-group-item">Info 2</li>
<li class="list-group-item">Info 3</li>
</div>
</div>
<div class="col-md-9">
<div class="card mb-3">
<img class="card-img-top" src="<%= component.image %>" alt="<%= component.name %>">
<div class="card-body">
<p class="float-right">
This item costs $<%= component.price %>
</p>
<h5 class="card-title"><%= component.name %></h5>
<p>
<em>Submitted By <%= component.author.username %>, <%= moment(component.createdAt).fromNow() %></em>
</p>
<hr>
<p class="card-text"><%= component.description %></p>
<% if(currentUser && component.author.id.equals(currentUser._id) || currentUser && currentUser.isAdmin){ %>
<a class="btn btn-xs btn-warning" href="/components/<%= component._id %>/edit">Edit</a>
<form class="delete-form" action="/components/<%= component._id %>?_method=DELETE" method="post">
<button class="btn btn-xs btn-danger">Delete</button>
</form>
<% } %>
</div>
</div>
<div class="card">
<div class="card-body">
<a class="btn btn-success" href="/components/<%= component._id %>/comments/new">Add a Comment</a>
</div>
<hr>
<% component.comments.forEach(function(comment){ %>
<div class="row">
<div class="col-md-12">
<strong><%= comment.author.username %></strong>
<span class="pull-right"><%= moment(comment.createdAt).fromNow() %></span>
<%= comment.text %>
<% if(currentUser && comment.author.id.equals(currentUser._id)) || currentUser && currentUser.isAdmin){ %>
<a class="btn btn-xs btn-warning"
href="/components/<%= component._id %>/comments/<%= comment._id %>/edit">Edit</a>
<form class="delete-form" action="/components/<%= component._id %>/comments/<%= comment._id %>?_method=DELETE" method="post">
<input type="submit" class="btn btn-xs btn-danger" value="Delete">
</form>
</div>
<% } %>
</div>
</div>
<% }); %>
</div>
</div>
<%- include ("../partials/footer") %>
And my routes/components.js file looks like this:
var express = require("express");
var router = express.Router();
var Component = require("../models/component");
var Comment = require("../models/comment");
var middleware = require("../middleware");
var {isLoggedIn, checkComponentOwnership, checkCommentOwnership, isAdmin} = middleware;
// Defining escapeRegex for search feature
function escapeRegex(text) {
return text.replace
};
//INDEX- show inventory
router.get("/", function(req, res){
if (req.query.search && req.xhr) {
var regex = new RegExp(escapeRegex(req.query.search), 'gi');
//Getting Inventory
Component.find({name: regex}, function(err, allComponents){
if(err){
console.log(err);
} else {
res.status(200).json(allComponents);
}
});
} else {
// Get Items from DB
Component.find({}, function(err, allComponents){
if(err){
console.log(err);
} else {
if(req.xhr) {
res.json(allComponents);
}
else {
res.render("components/components", {components: allComponents, page: 'components'});
}
}
});
}
});
// CREATE- Add Components to DB
router.post("/", isLoggedIn, function(req, res){
//Get form data and add to array- add here with invSchema
var name = req.body.name;
var image = req.body.image;
var desc = req.body.description; //Add stuff from invSchema here
var author = {
id : req.user._id,
username: req.user.username
}
var cost = req.body.cost;
var newComponent = {name: name, image: image, cost: cost, description: desc, author:author};
// Create a new component
Component.create(newComponent, function(err, newlyAdded){
if(err){
console.log(err);
} else {
//redirect back to Inventory page
console.log(newlyAdded);
res.redirect("/components");
}
});
});
// NEW= Take to Inventory form
router.get("/new", isLoggedIn, function(req, res){
res.render("components/new");
});
//SHOW- shows info on specific component
router.get("/:id", function(req, res){
//find component by idea
Component.findById(req.params.id).populate("comments").exec(function(err, foundComponent){
if(err){
console.log(err);
req.flash('error', 'No components are listed under that name.');
return res.redirect('/components');
} else {
console.log(foundComponent)
//render show template with that component
res.render("components/show", {component: foundComponent});
}
});
});
//EDIT COMPONENT ROUTE
router.get("/:id/edit", checkComponentOwnership, isLoggedIn, function(req, res){
//Find the component
Component.findById(req.params.id, function(err, foundComponent){
if(err){
console.log(err);
req.flash('error', 'Unable to edit component')
} else{
res.render("components/edit", {component: foundComponent});
}
});
});
// UPDATE COMPONENT ROUTE
router.put("/:id", checkComponentOwnership, function(req, res){
var newData = {name: req.body.name, image: req.body.image, cost: req.body.cost, description: req.body.description};
Component.findByIdAndUpdate(req.params.id, {$set: newData}, function(err, component){
if(err){
req.flash("error", err.message)
res.redirect("back");
} else {
req.flash("success", "Component has been updated successfully")
res.redirect("/components/" + req.params.id);
}
});
});
// DESTROY Route
router.delete("/:id", checkComponentOwnership, isLoggedIn, function(req, res){
Comment.remove({
_id: {
$in: req.component.comments
}
}, function(err) {
if(err){
req.flash('error', err.message);
res.redirect('/');
} else{
req.component.remove(function(err) {
if(err){
req.flash('error', err.message);
res.redirect('/');
}
req.flash('error', 'Component Removed');
res.redirect('/components');
});
}
});
});
module.exports = router;
Any ideas what I could be doing wrong?
On line 45, there's an extra closing parenthesis after the comment.author.id.equals(currentUser._id) call.
<% if(currentUser && comment.author.id.equals(currentUser._id)) || currentUser && currentUser.isAdmin){ %>
<!-- ^^^^ -->
<a class="btn btn-xs btn-warning"
href="/components/<%= component._id %>/comments/<%= comment._id %>/edit">Edit</a>
<form class="delete-form" action="/components/<%= component._id %>/comments/<%= comment._id %>?_method=DELETE" method="post">
<input type="submit" class="btn btn-xs btn-danger" value="Delete">
</form>
</div>
<% } %>
The error message also suggests using ejs-lint to get more helpful error messages. I would recommend trying it out. In this case, it printed:
Unexpected token (45:81) in test.ejs
I am trying to delete elements after retrieving them from the database using mongoose. However i am stuck at a part where i do not know how to 'grab' one particular element in the list and then how to delete it.
In my app, i have a list of users and their age.
Here is my userview.ejs (EDITED after adding frontend.js)
<meta charset="UTF8">
<link href="../public/javascripts/frontend.js">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="/stylesheets/userlist.css">
<link href='//fonts.googleapis.com/css?family=Amatic SC' rel='stylesheet'>
<link href='//fonts.googleapis.com/css?family=NTR' rel='stylesheet'>
<html>
<head>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<title>Userlist</title>
<script src="javascripts/frontend.js"></script>
<script>
</script>
</head>
<div class="container-fluid">
<div class="row">
<h1><strong>FORM</strong></h1>
<hr id="hr2" style="border: 6px solid palevioletred" >
<div id="black">
<form class="form-horizontal" method="post" action="/users">
<fieldset>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="textinput">Name : </label>
<div class="col-md-4">
<input id="textinput" name="name" placeholder="Enter Username" class="form-control input-md" type="text" value="Name" onfocus="if (this.value=='Name') this.value='';">
</div>
</div>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="textinput">Age : </label>
<div class="col-md-4">
<input id="textinput" name="age" placeholder="Enter Age" class="form-control input-md" type="number">
</div>
</div>
<!-- Button -->
<!-- Button (Double) -->
<div class="form-group">
<div class="col-md-8">
<button id="singlebutton" name="button1id" class="btn btn-success">Add User</button>
</div>
</div>
<body>
<h1><strong>USERS</strong></h1>
<hr id="hr1" style="border: 6px solid #7ec4ec;" >
<ul id="list" class="triangle">
<% for(var i=0; i<userlist.length; i++) {%>
<li><%= userlist[i].name %> : <%= userlist[i].age %> Delete || Update</li>
<% } %>
</ul>
</body>
</fieldset>
</form>
</div>
</div>
</div>
</html>
EDIT:
This is my frontend.js
var $ = require('jquery');
function delete_element (userId) {
$.post('delete/user/',userId,function(){
alert('Deleting....'); //For now i have just added an alert.
});
}
This is my new users.js
'use strict'
var express = require('express');
var router = express.Router();
var User=require('../models/usermodel.js');
var $= require('jquery');
/* GET users listing. */
router.get('/', function(req, res, next) {
User.find({},function(err,userModel){
res.render('userview',{userlist:userModel});
});
});
router.post('/', function(req, res, next) {
var newUser = new User({
name:req.body.name,
age: req.body.age
});
console.log(newUser);
// save the user
newUser.save(function(err) {
if (err) throw err;
console.log('User created!');
});
});
router.post("delete/user/:id", deleteUser);
function deleteUser(req,res){
User.findById(req.params.id).remove().exec(function(err){
if (!err) {
console.log('Removed Successfully');
}
else {
console.log('Error in removing the entry');
}
});
}
/*
function delete_element(id){
$('#delete').on("click",function(){
$(this).parent().remove();
});
User.remove({_id: req.body.id }, function(err) {
if (!err) {
console.log('Removed Successfully');
}
else {
console.log('Error in removing the entry');
}
});
}
*/
/*router.post('/', function(req, res, next) {
userModel.update( {name: req.params.name}, { $pullAll: {uid: [req.params.deleteUid] } } )
});*/
module.exports = router;
Embed the user's _id in your delete link :
onclick="delete_element('<%= userlist[i]._id %>')"
This will produce :
onclick="delete_element('55510c6cf0e19f6414b30f97')"
and pass the user's _id to your delete function.
Edit
Okay so, apparently, you're trying to call a function defined on the server (Node), directly from your view (HTML).
That's absolutely not how it works :)
onclick="delete_element()" is calling a function locally, in your browser. The console (F12) must yell error : delete_element is undefined.
So the way to go is the following :
1) Create a javascript function (not in Node, in your front-end application) that will call the server.
function delete_element (userId) { // This will be called on click and passed the user's _id
$.post('delete/user/'+userId) // This calls the server. Todo : add a callback, etc.
}
2) Your Node server must have a corresponding route that will receive the call :
router.post("delete/user/:id", deleteUser); // This is reached when the client calls post('delete/user/1a2b3c4d'). The id is read with req.params.id
deleteUser = (req,res) => {
User.findById(req.params.id)
.remove()
.exec( error => { // Reply to the client, otherwise the request will hang and timeout.
if(error) return res.status(500).send(error);
res.status(200).end()
})
}
That's for the first question, please create a new question for the second one.