Node/Express Routing issue - invoking incorrect path - javascript

Everything seems to be working fine, however when attempting to reach '/posts/new' I get "TypeError: Cannot read property 'title' of null" referencing '/routes/posts.js:24' which is in the '/:title' GET method. Any ideas why?
'routes/posts.js'
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var Post = mongoose.model('posts');
/* posts displays*/
// posts index
router.get('/', (req, res) => {
Post.find((err, posts) => {
res.render(
'posts/index',
{
title: 'Get All the Posts!',
posts: posts
}
);
});
});
// single post
router.get('/:title', (req, res) => {
var query = {"title": req.params.title};
Post.findOne(query, (err, post) => {
res.render(
'posts/post',
{
title: post.title,
date: post.date,
body: post.body
}
);
});
});
/* posts new */
router
// GET new posts route and form
.get('/new', (req, res) => {
res.render('posts/new', { title: 'Add a new Post' });
})
// POST new post data
.post('/new', (req, res) => {
new Post({
title: req.body.title,
date: req.body.date,
body: req.body.body
})
// Save post to db
.save((err, post) => {
res.redirect('/posts');
});
});
/* posts edit */
router
// GET the post
.get('/edit/:title', (req, res) => {
var query = {'title': req.params.title};
Post.findOne(query, (err, post) => {
res.render(
'posts/edit',
{
title: post.title,
date: post.date,
body: post.body
}
);
});
})
// PUT to update the post
.put('/edit/:title', (req, res) => {
var query = {'title': req.params.title};
var update = {
title: req.body.title,
body: req.body.body
};
var options = {new: true};
Post.findOneAndUpdate(query, update, options, (err, post) => {
res.render(
'posts/post',
{
title: post.title,
date: post.date,
body: post.body
}
);
});
})
// DELETE to delete a post
.delete('/edit/:title', (req, res) => {
var query = {'title': req.params.title};
Post.findOneAndRemove(query, (err, posts) => {
res.redirect('/');
})
});
module.exports = router;
'views/posts/new' with Swig:
{% extends '../layout.html' %}
{% block title %}{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
<form method='post' action='/posts/new' class='form-posts'>
<label>Title:</label>
<input type="text" name='title' class='form-posts-text' required>
<label>Body</label>
<input type="text" name='body' class='form-posts-body' required>
<button type='submit' class='btn-submit'>Submit</button>
</form>
{% endblock %}

Try placing the route for /new before the generic /:title root.
Because /:title is defined first, it gets called first if it matches (which it does). Since you have no post with the name "new", your database search comes up empty. You should probably have an error handler there. The error seems to be on the line title: post.title.
If /new is defined first, it will get called if it matches. If it doesn't, the more generic /:title will be.

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") -%>

How do I get my express app to refresh data from mongodb server without having to refresh the page?

I'm having trouble with getting blog posts to show up on the home page of my express blog app...they get saved (confirmed using mongo shell), but what happens after I click to save a composed post is the app will redirect to an empty home page (except for the default top material). That is, the short version of the post I just composed doesn't appear until I refresh the page manually.
My code:
mongoose.connect('mongodb://localhost:27017/blogDB', {
useNewUrlParser: true,
useUnifiedTopology: true
});
const postSchema = new mongoose.Schema({
title: {
type: String,
required: [true, 'missing blog post title']
},
content: {
type: String,
required: [true, 'missing blog post content']
}
});
const Post = mongoose.model('Post', postSchema);
app.get('/', (req, res) => {
Post.find({}, (err, returnedItems) => {
if (!err) res.render('home', { homeStartingContent, returnedItems });
});
});
app.post('/compose', (req, res) => {
const post = new Post({
title: req.body.postTitle,
content: req.body.postBody
});
post.save();
res.redirect('/');
});
/* ----------------------------------------------------------- */
<%- include('partials/header'); %>
<h1>Home</h1>
<p><%= homeStartingContent %></p>
<% returnedItems.map(post => { %>
<h1><%= post.title %></h1>
<p>
<%= `${post.content.substr(0, 100)} ...` %>
Read More
</p>
<% }); %> <%- include('partials/footer'); %>
When I console log the array length returned from my database - right after post.save() - the length is zero. So, I'm not sure why this is happening. It could have something to do with asynchronous code, but I wrote another app earlier that used the same type of code seen here without a problem.
Edit: I fixed this with async-await using this syntax:
app.post('/compose', async(req, res) => {
const post = new Post({
title: req.body.postTitle,
content: req.body.postBody
});
await post.save();
res.redirect('/');
});

Im trying to update my SQL data base with a Sequelize post request with express

I'm trying to update my SQL data base with a Sequelize post request with express. However When I clicked the Update but in respective route it does not work. The app is a simple online library app that lets the user create, update and delete books. I cant get the update or delete parts of the app to work. Being that both of these are post routes I'm assuming I'm doing something very wrong here.
routes/books.js
const express = require('express');
const router = express.Router();
const Book = require('../models').Book;
function asyncHandler(cb){
return async(req, res, next) => {
try {
await cb(req, res, next)
} catch(error){
res.status(500).send(error);
}
}
}
// Shows the full list of books.
router.get('/', asyncHandler(async (req, res, next) => {
try {
const books = await Book.findAll({ order: [["createdAt", "DESC"]]});
res.render('index', { books, title: "Book list" });
console.log('Rendering books');
} catch(e) {
console.log(e);
}
}));
// Shows the create new book form.
router.get('/new', asyncHandler(async (req, res) => {
res.render('new-book', {book: {}, title: "New Book"});
}));
/// Posts a new book to the database and redirects to the new route.
router.post('/', asyncHandler(async (req, res, next) => {
const book = await Book.create({
title: req.body.title,
author: req.body.author,
genre: req.body.genre,
year: req.body.year })
res.redirect("/books/" + book.id);
console.log('Posting books new');
}));
// Shows book detail form.
router.get("/:id", asyncHandler(async (req, res) => {
const book = await Book.findByPk(req.params.id);
if(book) {
res.render("book-detail", { book, title: book.title });
} else {
res.sendStatus(404);
}
}));
// Updates book info in the database.
router.post('/', asyncHandler(async (req, res) => {
const book = await Book.findByPk(req.params.id);
if (book) {
await book.update(req.body);
res.redirect("/books");
} else {
res.sendStatus(404);
}
}));
// Deletes a book.
router.post('/:id/delete', asyncHandler(async (req ,res) => {
const book = await Book.findByPk(req.params.id)
if(book) {
await book.destroy();
res.redirect("/books");
} else {
res.sendStatus(404);
}
}));
module.exports = router;
routes/index.js
const express = require('express');
const router = express.Router();
/* GET home page. */
router.get('/', (req, res, next) => {
res.redirect("/books")
});
module.exports = router;
model/book.js (This is the module for the sequelize properties)
const express = require('express');
const Router = require('Router');
const Sequelize = require('sequelize');
'use strict';
module.exports = (sequelize) => {
class Book extends Sequelize.Model {
}
Book.init({
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
title: {
type: Sequelize.STRING,
allowNull: false, // disallow null
validate: {
notEmpty: {
msg: 'Please provide a value for "title"'
}
}
},
author: {
type: Sequelize.STRING,
allowNull: false, // disallow null
validate: {
notEmpty: {
msg: 'Please provide a value for "author"'
}
}
},
genre: {
type: Sequelize.STRING,
allowNull: false, // disallow null
validate: {
notEmpty: {
msg: 'Please provide a value for "genre"'
}
}
},
year: {
type: Sequelize.INTEGER,
allowNull: false, // disallow null
validate: {
notEmpty: {
msg: 'Please provide a value for "year"'
}
}
}
}, { sequelize })
return Book
}
views/book-detail.pug
extends layout.pug
block content
h1(class='title') Update Book
form(action="/books/" + book.id, method="post")
p
label(for='title')= book.title
input#title(name='title' type='text' value= book.title)
p
label(for='author')= book.author
input#author(name='author' type='text' value= book.author)
p
label(for='genre')= book.genre
input#genre(name='genre' type='text' value= book.genre)
p
label(for='year')= book.year
input#year(name='year' type='text' value= book.year)
p
input(type='submit' value='Update Book' method='post')
form(method="post", action="/books/" + book.id, onsubmit="return confirm('Do you really want to delete this book?');")
p
a.button(href='/') ← Home
p
input(type='submit' value='Delete Book')
I think the problem is that your primaryKey is an integer, and req.params.id is a string. You'll need to parse it first. Once you do that, sequelize should be able to find the Book instance you're looking for.

EJS / Mongoose - Posts tend to show twice after POST, when I try to GET the same page, it shows up twice?

Well basically when I POST the create post controller, the post shows up once, the problem is when I GET the same page it shows up twice?
How can I solve this to make so that if the post already exists, then it should not show another time even tho I GET the page?
My goal is that altho the user post or gets the page, it should still be showing once if it exists.
admin controller
const path = require('path');
const bcrypt = require("bcryptjs");
const mongoose = require("mongoose");
const _ = require("lodash");
const {
User,
Post,
} = require("../models/model");
exports.getMyPostsPage = (req, res) => {
res.render("admin/myposts", {
path: "/myposts",
pageTitle: "My Posts",
})
}
exports.getCreatepostPage = (req, res) => {
res.render("admin/createpost", {
path: "/create",
pageTitle: "Create",
});
}
exports.getPostsPage = async (req, res) => {
try {
const posts = await Post.find({})
res.render("admin/posts", {
path: "/posts",
pageTitle: "Posts",
posts: posts
});
} catch (err) {
console.log(err);
}
}
exports.postCreatePost = async (req, res) => {
const {
title,
description,
context
} = req.body;
const post = new Post({
title,
description,
context,
author: req.user
});
try {
const savedPost = await post.save()
const usersPost = await req.user.posts.push(post);
console.log(req.user.posts)
const posts = await Post.find({})
res.render("admin/posts", {
pageTitle: "Posts",
path: "/posts",
posts: posts
})
} catch (err) {
console.log(err);
}
}
posts.ejs
<%- include("../includes/head.ejs") %>
<link rel="stylesheet" href="/css/admin.css">
</head>
<body>
<%- include("../includes/navigation.ejs", ) %>
<% for (const post of posts) { %>
<div class="posts">
<form action="/post" method="POST">
<h1><%=post.title%></h1>
<p><%=post.description%></p>
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<button type="submit">
See Post
</button>
</form>
</div>
<% } %>
<%- include("../includes/footer.ejs") %>
const mongoose = require("mongoose"),
Schema = mongoose.Schema,
bcrypt = require("bcryptjs");
const postSchema = new Schema({
title: String,
description: String,
context: String,
author: {
type: Schema.Types.ObjectId,
}
});
const userSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true
},
posts: [postSchema]
});
userSchema.pre("save", async function save(next) {
const user = this;
if (!user.isModified("password")) return next();
const hashedPassword = await bcrypt.hash(user.password, 10);
user.password = hashedPassword;
next();
});
const Post = mongoose.model("Post", postSchema);
const User = mongoose.model("User", userSchema);
module.exports = {
User,
Post
}
I had an issue when it came to using the f5 button, it repeated the POST part, but it is fine now, it was actually not a real issue just a bug so to speak.

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