Cannot read property items of null - javascript

I am learning javascript and databases, so I am practicing by making a todo list application. Which add item dynamically in the lists. List is created through URL like http://localhost:3000/work creates a work list, but whenever I try to add something it shows an error.
my JS code:
const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const app = express();
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({extended: true}));
app.use(express.static("public"));
mongoose.connect("mongodb://localhost:27017/todolistDB", {useNewUrlParser: true, useUnifiedTopology: true });
const itemsSchema = {
name: String
};
const Item = mongoose.model ("Item", itemsSchema);
const item1 = new Item ({
name: "item1"
});
const item2 = new Item ({
name: "item2"
});
const item3 = new Item ({
name: "item3"
});
const defaultItems = [item1, item2, item3];
const listSchema = {
name: String,
items: [itemsSchema]
};
const List = mongoose.model("List", listSchema);
app.get("/", function(req, res) {
Item.find({}, function(err, foundItems){
if(foundItems.length === 0){
Item.insertMany(defaultItems, function(err){
if(err){
console.log(err);
} else {
console.log("Succesfully saved the items");
}
});
res.redirect("/");
} else {
res.render("list", {listTitle: "Today", newListItems: foundItems});
}
});
});
app.get("/:customListName", function(req, res){
const customListName = req.params.customListName;
List.findOne({name: customListName}, function(err, foundList){
if(!err){
if(!foundList){
//Create a new list
const list = new List({
name: customListName,
items: defaultItems
});
list.save();
res.redirect("/" + customListName);
} else{
//show the existing list
res.render("list", {listTitle: foundList.name, newListItems: foundList.items})
}
}
});
});
app.post("/", function(req, res){
const itemName = req.body.newItem;
const listName = req.body.list;
const item = new Item ({
name: itemName
});
if(listName === "Today"){
item.save();
res.redirect("/");
} else {
List.findOne({name: listName}, function(err, foundList){
if(err){
console.log(err);
} else{
foundList.items.push(item);
foundList.save();
res.redirect("/" + listName);
}
});
}
});
app.post("/delete", function(req, res){
const checkedItemId = req.body.checkbox;
Item.findByIdAndRemove(checkedItemId, function(err){
if(!err){
console.log("Succesfully deleted the item!!");
res.redirect("/");
}
});
});
app.get("/about", function(req, res){
res.render("about");
});
app.listen(3000, function() {
console.log("Server started on port 3000");
});
My ejs code:
<%- include("header") -%>
<div class="box" id="heading">
<h1> <%= listTitle %> </h1>
</div>
<div class="box">
<% newListItems.forEach(function(item) { %>
<form action="/delete" method="post">
<div class="item">
<input type="checkbox" name="checkbox" value="<%=item._id%>" onchange="this.form.submit()">
<p><%=item.name%></p>
</div>
</form>
<% }); %>
<form class="item" action="/" method="post">
<input type="text" name="newItem" placeholder="New Item" autocomplete="off">
<button type="submit" name="list" value="<%=listTitle%>">+</button>
</form>
</div>
<%- include("footer") -%>

The List.findOne function might return either one "List" { name: String, items: [itemsSchema]} item or null. Unfortunately, you don't check for the null condition which can happen if this list hasn't been created yet in your database.
To solve this issue, you'll need to add an existence check for the foundList variable in your code.
If the foundList variable isn't null, then the foundList contains a list and a new item can be pushed to it.
Else if the foundList variable is null, you can create a new list with this name and the item you want to add to it.
if (err) {
console.log(err);
} else if (foundList !== null) {
foundList.items.push(item);
foundList.save();
res.redirect("/" + listName);
} else {
// List is not found, here you can create a new list with the item, or possibly return an error to the user (listName not found!)
foundList = new List({ foundList: [item], name: listName });
foundList.save();
res.redirect("/" + listName);
}
PS: You could refactor the code I wrote above to reduce duplication.

Related

node.js Form data doesn't render and creates an empty object in mogoDb

Hey guys I'am trying to make a blog website in node.js with mongoDB. But the form data doesn't show on home page as it should. It shows some errors while running like:
in home.js it says
1. "Cannot read properties of undefined (reading 'substring')"
2. "blogDB> db.posts.find()
[
{ _id: ObjectId("62f5341c3fb15b1283671737"), __v: 0 },
{ _id: ObjectId("62f537843fb15b128367173a"), __v: 0 }
]"
empty objects gets created when submit button is clicked in the form
//app.js
const postSchema = new mongoose.Schema({
title: {
type: String
},
content: {
type: String
}
});
const Post = mongoose.model("Post", postSchema);
app.get("/", function (req, res) {
Post.find({}, function (err, foundposts) {
if (foundposts.length > 0) {
res.render("home", {
startingContent: homeStartingContent,
posts: foundposts
});
} else {
res.render('home', {
startingContent: homeStartingContent,
posts: []
});
}
});
});
app.get("/compose", function (req, res) {
res.render('compose');
});
app.post("/compose", function (req, res) {
const post = new Post({
title: req.body.newPostNewPage_Title,
content: req.body.newPostNewPage_Content
});
post.save(function (err) {
if (!err)
res.redirect("/");
});
});
app.get("/posts/:postId", function (req, res) {
const requestedPostId = req.params.postId;
posts.forEach(function (post) {
Post.findOne({_id: requestedPostId}, function(err, post){
res.render("post",{
title: post.title,
content: post.content
});
});
})
});
//home.js
<%- include("partials/header") -%>
<h1 style="color: rgb(118, 202, 70);">Home</h1>
<p>
<%=startingContent%>
</p>
<% posts.forEach(function(post){ %>
<h1>
<%=posts.title %>
</h1>
<p>
<%=post.content.substring(0, 100) + " ..." %>
Read More
</p>
<% }); %>
<%- include("partials/footer") -%>

server breaks even though I handle the errors

my index.js file and views/products/edit.ejs files :
const express = require('express')
const path = require('path')
const mongoose = require('mongoose');
const methodOverride = require('method-override')
const app = express()
app.set('view engine', 'ejs')
app.set('views', path.join(__dirname, "views"))
app.use(express.urlencoded({
extended: true
}))
app.use(methodOverride('_method'))
mongoose.connect('mongodb://localhost:27017/farmStand')
.then((data) => {
console.log("successfully connected to database")
console.log(data)
}).catch(error => {
console.log("error occured")
console.log(error)
})
function wrapperASync(fn) {
return function(req, resp, next) {
fn(req, resp, next).catch((e) => {
next(e)
})
}
}
let categories = ['fruits', 'vegetables', 'dairy']
const Product = require('./models/product');
const {
param
} = require('express/lib/request');
app.get('/', (req, resp) => {
resp.send("getting data")
})
app.get('/products', async(req, resp) => {
const {
category
} = req.query
const selectedcategory = category
console.log(selectedcategory)
if (selectedcategory) {
let products = await Product.find({
category: selectedcategory
})
resp.render("products/index", {
products,
selectedcategory
})
} else {
let products = await Product.find({})
// console.log("found products")
resp.render("products/index", {
products,
selectedcategory: "All"
})
}
})
app.get('/products/:id', async(req, resp, next) => {
const {
id
} = req.params
let myproduct = await Product.findById(id)
if (!myproduct) {
return next(new AppError("product not found", 404))
}
// console.log(myproduct)
resp.render('products/details', {
myproduct
})
})
app.get('/product/new', (req, resp) => {
resp.render('products/new', {
categories
})
})
app.get('/products/:id/edit', wrapperASync(async(req, resp, next) => {
const {
id
} = req.params
const product = await Product.findById(id)
if (!product) {
throw new AppError("product not found", 404)
}
resp.render('products/edit', {
product,
categories
})
}))
app.put('/products/:id', wrapperASync(async(req, resp, next) => {
const {
id
} = req.params
const updatedValues = req.body
const newvalue = await Product.findByIdAndUpdate(id, updatedValues, {
runValidators: true,
new: true
})
// console.log(newvalue)
resp.redirect(`/products/${newvalue._id}`)
}))
app.post('/products', wrapperASync(async(req, resp) => {
const newdoc = req.body
const myproduct = new Product(newdoc)
console.log(myproduct)
await myproduct.save()
resp.redirect(`/products/${myproduct._id}`)
}))
app.delete('/products/:id', async(req, resp, next) => {
try {
const {
id
} = req.params
const deleted_product = await Product.findOneAndDelete(id)
resp.redirect('/products')
} catch (error) {
next(error)
}
})
app.use((err, req, resp, next) => {
console.log(err.name)
next(err)
})
app.use((err, req, resp, next) => {
const {
status = 404, message = "something went wrong"
} = err
resp.status(status).send(message)
})
app.listen(3000, () => {
console.log("server started successfully")
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1> enter product details</h1>
<form action="/products/<%= product.id %>?_method=PUT " method="POST">
<label for="name">enter the name of the product : </label>
<input type="text" name="name" id="name" placeholder="name" value="<%= product.name %> ">
<label for="price">enter the price in dollars : </label>
<input type="number" name="price" id="price" placeholder="price" value="<%= product.price %>">
<label for="category">category : </label>
<select name="category" id="category">
<% for(category of categories) { %>
<option value="<%= category %>" <%=p roduct.category===c ategory ? "selected": '' %> >
<%= category %>
</option>
<% } %>
</select>
<button>add new details</button>
</form>
<section>
exit
</section>
</body>
</html>
my models/product.js file :
const mongoose = require('mongoose')
const productSchema = new mongoose.Schema({
name : {
type : String,
required : true
},
price:{
type : Number,
required : true,
min : 0
},
category:{
type:String,
lowercase :true,
enum : ["fruits","vegetables","dairy"]
}
})
const Product = new mongoose.model('Product',productSchema);
module.exports = Product;
my problem : I have installed all the dependencies my problem is when I am entering in to the edit page and then when I type my id incorrect and of different length in the url for which Iam listening for I get the CastError printed out on my console and when I keep my name Field as blank and edit then I get the ValidationError printed out on my console but after getting the validation error when I change the id of the product which is incorrect again and which is of different length in the url after getting validation error response and send the request in chrome my server gets error and breaks even though I handle the error of my /products/:id put route
please help me

Submit a POST and process the result in Nodejs/EJS

I have an Index Site (EJS) that reads an array into a select form.
index.ejs
<html>
<head>
</head>
<body>
<center>
<form action="/dates" method="POST">
<b>INDEX:</b>
<select id="index" name="index" onchange="this.form.submit()">
<option id="0"> Please choose Index </option>
<% collectionnames.map(item=> { %>
<option id="<%= item._id%>"> <%= item %> </option>
<% }) %>
</select>
<br>
</form>
</body>
</html>
When the Index is chosen it posts the index value to /dates in app.js
app.js
var express = require('express');
var MongoClient = require('mongodb').MongoClient;
var app = express();
var bodyParser = require('body-parser');
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({ extended: false }));
app.use( bodyParser.json() );
app.use(express.static('public'));
app.get('/', function(req, res) {
var collectionnames = [];
MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true }, function (err, client) {
if (err) {
console.log("Error");
} else {
console.log("Connected to MongoDB to get collections...");
}
var db = client.db('my-mongodb');
db.listCollections().toArray(function (err,collections) {
collections.forEach(function(collection){
var elem = new Object();
if (collection.name !== "system.indexes"){
//console.log(collection.name);
elem = collection.name;
collectionnames.push(elem);
};
});
console.log("got collections...")
res.render('index.ejs' , { collectionnames: collectionnames } );
});
});
});
app.post('/dates', function (req, res) {
const postBody = req.body;
index = postBody.index;
var dates = [];
MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true }, function (err, client) {
if (err) {
console.log("Error");
} else {
console.log("Connected to fetch results");
}
var db = client.db('my-mongodb');
db.collection (postBody.index, function(err, values){
values.find().sort({VALUETIME:+1}).toArray( function (err,results){
results.forEach(function(result){
dates.push(result.VALUEDATE);
//console.log(elem)
});
console.log("got results...")
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
var unique = dates.filter( onlyUnique );
console.log(unique);
res.send(unique);
});
});
});
});
How can i just pass the array "unique" back to my index.ejs to process it in another select form? What happens right now (with res.send(unique)) is that i receive a list in the browser with the array values:
0 "2018-09-01"
1 "2018-09-02"
2 "2018-09-05"
3 "2018-09-03"
4 "2018-09-04"
5 "2018-08-31"
More specific: how to retrieve the array/values back to my index.ejs and do something with it?
Thanks.
You can use for loop on collectionnames like:
//Assuming your `collectionnames = ['2018-09-01', '2018-09-02', '2018-09-03', '2018-09-04']`
<select id="selectId">
<option value="">Select</option>
<% if (collectionnames.length) { %>
<% for (var i = 0; i < collectionnames.length; i++) { %>
<option value="<%=collectionnames[i]%>"><%=collectionnames[i] %> </option>
<% } %>
<% } %>
</select>
EDIT: How to send data back to same ejs page?
In order to use same ejs page you need to use Single Page Application approach. For single-page you need to submit data using Ajax call and not form-submit. Check out this repository and various example of how to implement single-page application.
Also if you don't want to go into single-page application just change your api response, render same page with new data:
app.post('/dates', function (req, res) {
const postBody = req.body;
index = postBody.index;
var dates = [];
MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true }, function (err, client) {
if (err) {
console.log("Error");
} else {
console.log("Connected to fetch results");
}
var db = client.db('my-mongodb');
db.collection (postBody.index, function(err, values){
values.find().sort({VALUETIME:+1}).toArray( function (err,results){
results.forEach(function(result){
dates.push(result.VALUEDATE);
//console.log(elem)
});
console.log("got results...")
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
var unique = dates.filter( onlyUnique );
console.log(unique);
// Change here
res.render('index.ejs' , { collectionnames: unique });
});
});
});
});

Read data from mongoose with node in Atom

i'm building a web page for a library with node and mongoose in atom. I've created a log in form, register and an "add book". Now in the home page i want to see all the books and i dont know to to do that. I know it way have something to do with double {{}} or triple {{{}}} brackets but I'm not sure. I will put it later in a prettyer form, for now i just want to see how I can print, for example the titles. Btw i'm using the handlebars view engine.
<h2 class="page-header">Cartile existente</h2>
<h4>In curand!</h4> // <- there I'm supposed to put the book titles.
<h2 class="page-header">Adauga o carte noua</h2>//this is the "add a book"
{{#if errors}}
{{#each errors}}
<div class="alert alert-danger">{{msg}}</div>
{{/each}}
{{/if}}
<form method="post" action="/booksImp/addbook">
<div class="form-group">
<label>Numar Inventar</label>
<input type="text" class="form-control" placeholder="Numar curent" name="nrinv">
</div>
<div class="form-group">
<label>Titlu</label>
<input type="text" class="form-control" placeholder="Titlu" name="titlu">
</div>
<div class="form-group">
<label>Autor</label>
<input type="text" class="form-control" placeholder="Autor" name="autor">
</div>
<div class="form-group">
<label>Editura</label>
<input type="text" class="form-control" placeholder="Editura, Locul publicatiei" name="editura">
</div>
<div class="form-group">
<label>An</label>
<input type="text" class="form-control" placeholder="An publicatie" name="an">
</div>
<div class="form-group">
<label>Pret</label>
<input type="text" class="form-control" placeholder="Pret" name="pret">
</div>
<button type="submit" class="btn btn-success">Trimite</button>
</form>
var mongoose = require('mongoose');
var bcrypt = require('bcryptjs');
var BookSchema = mongoose.Schema({
nrinv: {
type: String,
index: true
},
titlu: {
type: String
},
autor: {
type: String
},
editura: {
type: String
},
an: {
type: String
},
pret: {
type: String
}
});
var Book = module.exports = mongoose.model('bookImp', BookSchema);
module.exports.getBooks = (callback, limit) => {
Book.find(callback).limit(limit);
}
module.exports.getBookById = (id, callback) => {
Book.findById(id, callback);
}
module.exports.addBook = (book, callback) => {
Book.create(book, callback);
}
module.exports.removeBook = (id, callback) => {
var query = {
_id: id
};
BookImp.remove(query, callback);
}
var express = require('express'); //this is the controller code =))))
var router = express.Router();
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var Book = require('../models/bookImp');
router.get('/addbook', ensureAuthenticated, function(req, res, next) {
res.render('addbook');
});
router.post('/addbook', function(req, res, next) {
var nrinv = req.body.nrinv;
var titlu = req.body.titlu;
var autor = req.body.autor;
var editura = req.body.editura;
var an = req.body.an;
var pret = req.body.pret;
//console.log(name);
req.checkBody('nrinv', 'Introduceti numarul de inventar').notEmpty();
req.checkBody('titlu', 'Introduceti titlul').notEmpty();
req.checkBody('autor', 'Introduceti autorul').notEmpty();
req.checkBody('editura', 'Introduceti editura si locul').notEmpty();
req.checkBody('an', 'Introduceti anul').notEmpty();
// req.checkBody('an', 'Introduceti anul').isnumber();
req.checkBody('pret', 'Introduceti pretul').notEmpty();
var errors = req.validationErrors();
if (errors) {
res.render('addbook', {
errors: errors
});
} else {
var newBook = new Book({
nrinv: nrinv,
titlu: titlu,
autor: autor,
editura: editura,
an: an,
pret: pret
});
Book.addBook(newBook, function(err, book) {
if (err) throw err;
console.log(book);
});
req.flash('success_msg', 'Ati adaugat o carte cu succes');
res.redirect('/booksImp/addbook');
}
});
router.post('/addbook',
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/booksImp/addbook',
failureFlash: true
}),
function(req, res) {
res.redirect('/');
});
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
} else {
//req.flash('error_msg', 'Trebuie sa va logati');
res.redirect('/users/login');
}
}
module.exports = router;
var express = require('express');
var router = express.Router();
var Book = require('../models/bookImp');
router.get('/', ensureAuthenticated, function(req, res, next) {
res.render('index');
});
router.get('/api/books', ensureAuthenticated, function(req, res) {
Book.getBooks(function(err, books) {
if (err) {
throw err;
}
res.json(books);
});
});
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
} else {
//req.flash('error_msg', 'Trebuie sa va logati');
res.redirect('/users/login');
}
}
module.exports = router;

ExpressJS POST Method

I am running into an issue where my POST route from a form submissions redirects correctly without any issues, but none of the information is passed on to a document in database. I am not sure the best way to debug this issue.
blogpost-create.ejs
<html>
<head>
<% include ../partials/head %>
<script src="//cdn.ckeditor.com/4.4.5/standard/ckeditor.js"></script>
</head>
<body>
<header>
<% include ../partials/header %>
</header>
<div class="grid grid-pad">
<div class="col-1-1">
<h1>Blog Create</h1>
<form action="/admin/posts/create" method="POST">
Title: <input type="text" name="title"><br>
Author:
<select name="author">
<option value="Author">Author</option>
</select><br>
Category:
<select name="category">
<option value="Analytics/SEO/SEM">Analytics/SEO/SEM</option>
<option value="Advice">Advice</option>
<option value="Programming">Programming</option>
<option value="Thoughts">Thoughts</option>
</select><br>
Tagline: <input type="text" maxlength="160" name="tagline"><br>
Content:<br>
<textarea name="content" id="blog-editor" rows="10" cols="80">
Text editor.
</textarea><br>
Tags: <input type="text" name="tags"><br>
<input type="submit" value="Submit">
</form>
</div>
</div>
<script>
// Replace the <textarea id="blog-editor"> with a CKEditor
// instance, using default configuration.
CKEDITOR.replace( 'blog-editor' );
</script>
<footer>
<% include ../partials/footer %>
</footer>
</body>
</html>
routes.js
var express = require('express');
var router = express.Router();
var blogDB = require('../config/blogDB.js');
var Blogpost = require('./models/blogModel.js');
var paginate = require('express-paginate');
//index
router.use(paginate.middleware(10, 50));
router.route('/')
// START GET method
.get(function(req, res, next) {
Blogpost.paginate({}, req.query.page, req.query.limit, function(err, pageCount, blogpost, itemCount) {
if (err) return next(err)
if (err)
res.send(err);
blogpost.title = req.body.title; // get the blog title
blogpost.author = req.body.author; // get the author name
blogpost.tagline = req.body.tagline; // get tagline
blogpost.content = req.body.content; // get the blog content
blogpost.category = req.body.category; // get the category
blogpost.tags = req.body.tags; // get the tags
res.format({
html: function() {
res.render('pages/index', {
blogpost: blogpost,
pageCount: pageCount,
itemCount: itemCount
})
},
json: function() {
res.json({
object: 'blogpost',
has_more: paginate.hasNextPages(req)(pageCount),
data: blogpost
})
}
}); // END res.format(html, json)
}); // END Blogpost.paginate
}); // END GET method
router.route('/admin/posts/create')
// START POST method
.post(function(req, res) {
var blogpost = new Blogpost(); // create a new instance of a Blogpost model
blogpost.title = req.body.title; // set the blog title
blogpost.author = req.body.author; // set the author name
blogpost.tagline = req.body.tagline; // set the tagline
blogpost.content = req.body.content; // set the blog content
blogpost.category = req.body.category; // set the category
blogpost.tags = req.body.tags; // set the tags
//Save Blog Post
blogpost.save(function(err) {
if (err)
res.send(err);
res.redirect(303, '/'); //NEEDS TO BE CHANGED
});
}) // END POST method
.get(function(req, res) {
res.render('pages/blogpost-create');
});
function getSearchCriteria(params) {
return {
title: params.blogpost_title
};
}
function getBlogpostUpdate(body) {
return {
title: body.title,
author: body.author,
tagline: body.tagline,
content: body.content,
category: body.category,
tags: body.tags
};
}
var blogpostsRoute = router.route('/blog/:blogpost_title');
// to manipulate your route params, use router.param
router.param('blogpost_title', function (req, res, next, blogpost_title) {
req.param.blogpost_title = blogpost_title.toLowerCase();
next();
});
blogpostsRoute
.get(function (req, res) {
var searchCriteria = getSearchCriteria(req.params);
Blogpost.findOne(searchCriteria, function (err, blogpost) {
if (err)
res.send(err);
res.render('pages/blogpost', {
blogpost: blogpost
})
})
})
.put(function (req, res) {
var searchCriteria = getSearchCriteria(req.params);
var updated = getBlogpostUpdate(req.body)
Blogpost.findOneAndUpdate(searchCriteria, updated, function (err, updated) {
if (err)
res.send(err);
res.json({ message: 'Blog updated.' });
});
})
.delete(function (req, res) {
var searchCriteria = getSearchCriteria(req.params);
Blogpost.findOneAndRemove(searchCriteria, function (err, removed) {
if (err)
res.send(err);
res.json({ message: 'Successfully deleted' });
});
});
//about
router.get('/about', function(req, res) {
res.render('pages/about');
});
//resume
router.get('/resume', function(req, res) {
res.render('pages/resume');
});
module.exports = router;
blogModel.js
var mongoose = require('mongoose');
var mongoosePaginate = require('mongoose-paginate');
var Schema = mongoose.Schema;
var BlogPostSchema = new Schema({
title: String,
author: String,
tagline: String,
category: String,
content: String,
tags: { type: String, lowercase: true },
date: { type: Date, default: Date.now }
});
BlogPostSchema.plugin( mongoosePaginate );
var Blogpost = mongoose.model("Blogpost", BlogPostSchema);
module.exports = mongoose.model('Blogpost', BlogPostSchema);
Your form is missing content (currently you have the name as blog-editor), author, and category fields (the last two are the <select>s but they're missing names and their <option>s are missing value attributes. Other than that you have maxlength:"160" which should probably be maxlength="160".

Categories

Resources