Why is my variable undefined in the template? - javascript

i get the this error
121| <div class="container-fluid bg-dark text-white" style="margin-top: 2%;">
122|
>> 123| <h1 class="mb-1 text-center"><%= article.title %> </h1>
124| </div>
125| <div class="container-fluid blog-footer">
126| <p>Simqle Team | Bütün Hakları Saklıdır | Hakkımızda <b> | </b>Nedim Talha.</p>
article is not defined
on this code
const express = require('express');
const router = express.Router();
const Article = require('../models/article');
router.get('/new', (req, res) => {
res.render('articles/new', { article: new Article() })
})
router.get('/:id', async(req, res) => {
const article = await Article.findById(req.params.id)
if(article == null) res.redirect('/')
res.render('articles/show')
})
router.post('/', async(req, res) => {
var article = new Article({
title: req.body.title,
description: req.body.description,
markdown: req.body.markdown
})
try {
article = await article.save()
res.redirect(`/articles/${article.id}`)
} catch(err) {
res.render('articles/new', { article: article })
}
})
module.exports = router
I could not solve this error

You're not passing the article in render
it should be like this
res.render('articles/show', { article })

Related

Failed to load resource: the server responded with a status of 500 (Internal Server Error) - NodeJs Express & MongoDB Web Application

I'm building a NodeJS Express & MongoDB Web application.
However, since I'm trying to use some AJAX feature to load and display comments on the post page, I'm getting an issue and the "post-detail" page is not displaying.
My code seems fine though. So, I'm not able to find where is the mistake.
When I check this error message:
Failed to load resource: the server responded with a status of 500
(Internal Server Error)
Is there someone who can check my code and let me know what's wrong with it?
I think there is a problem in the route or the paths?
app.js:
const path = require('path');
const express = require('express');
const db = require('./data/database');
const adminRoutes = require('./routes/admin/blog');
const defaultRoutes = require('./routes/home/default');
const postsRoutes = require('./routes/home/posts');
const quotationsRoutes = require('./routes/home/quotations');
const contactsRoutes = require('./routes/home/contacts');
const app = express();
app.set('views', [
path.join(__dirname, 'views/home'),
path.join(__dirname, 'views/admin')
]);
app.set('view engine', 'ejs');
app.use(express.static('public'));
app.use('/public/admin/images', express.static('public/admin/images'));
app.use(express.urlencoded({ extended: true }));
app.use('/', adminRoutes);
app.use('/', defaultRoutes);
app.use('/', postsRoutes);
app.use('/', quotationsRoutes);
app.use('/', contactsRoutes);
app.use(function (req, res) {
res.status(404).render('404');
});
app.use(function (error, req, res, next) {
res.status(500).render('500');
});
db.connectToDatabase().then(function () {
app.listen(3000);
});
routes\home\posts.js:
const express = require('express');
const mongodb = require('mongodb');
// const uuid = require('uuid');
const db = require('../../data/database');
const ObjectId = mongodb.ObjectId;
const router = express.Router();
router.get('/blog', async function (req, res) {
const posts = await db
.getDb()
.collection('posts')
.find({})
.project({ title: 1, summary: 1, 'author.name': 1, imagePath: 1 })
.toArray();
res.render('posts', { posts: posts });
});
router.get('/blog/:id', async function (req, res, next) {
let postId = req.params.id;
try {
postId = new ObjectId(postId);
} catch (error) {
return res.status(404).render('404');
// return next(error);
}
const post = await db
.getDb()
.collection('posts')
.findOne({ _id: postId }, { summary: 0 });
if (!post) {
return res.status(404).render('404');
}
post.humanReadableDate = post.date.toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
});
post.date = post.date.toISOString();
res.render('post-detail', { post: post });
});
router.get('/blog/:id/comments', async function (req, res) {
const postId = new ObjectId(req.params.id);
const comments = await db
.getDb()
.collection('comments')
.find({ postId: postId })
.toArray();
res.json(comments);
});
router.post('/blog/:id/comments', async function (req, res) {
const postId = new ObjectId(req.params.id);
const newComment = {
postId: postId,
title: req.body.title,
text: req.body.text
};
await db.getDb().collection('comments').insertOne(newComment);
res.redirect('/blog/' + req.params.id);
});
module.exports = router;
views\home\post-detail.ejs:
<!DOCTYPE html>
<html lang="en">
<head>
<%- include('../admin/includes/head', { title: 'Post title' }) %>
<link rel="stylesheet" href="/admin/styles/posts.css" />
<link rel="stylesheet" href="/admin/styles/forms.css" />
<script src="/home/scripts/comments.js" defer></script>
</head>
<body>
<%- include('../admin/includes/header') %>
<main id="post-detail">
<article class="post-item">
<img src="/<%= post.imagePath %>" alt="<%= post.title %>" />
<h1><%= post.title %></h1>
<section id="post-meta">
<address>
<a href="mailto:<%= post.author.email %>"
><%= post.author.name %></a
>
</address>
|
<time datetime="<%= post.date %>"><%= post.humanReadableDate %></time>
</section>
<hr />
<section>
<p id="body"><%= post.body %></p>
</section>
</article>
<section id="comments">
<% if (!comments) { %>
<p>
This post might have comments. You can load them if you want to view
them.
</p>
<button
id="load-comments-btn"
class="btn btn-alt"
data-postid="<%= post._id %>"
>
Load Comments
</button>
<% } else if (comments.length === 0) { %>
<p>No comments found.</p>
<% } else { %>
<ol>
<% for (const comment of comments) { %>
<li><%- include('includes/comment-item', { comment: comment }) %></li>
<% } %>
</ol>
<% } %>
</section>
<section id="comments-form">
<h2>Leave a comment</h2>
<form action="/posts/<%= post._id %>/comments" method="POST">
<div class="form-control">
<label for="title">Comment title</label>
<input type="text" id="title" name="title" required />
</div>
<div class="form-control">
<label for="text">Your comment</label>
<textarea name="text" id="text" rows="3" required></textarea>
</div>
<button class="btn">Save Comment</button>
</form>
</section>
</main>
</body>
</html>
views\home\includes\comments\comment-item.ejs:
<article class="comment-item">
<h2><%= comment.title %></h2>
<p><%= comment.text %></p>
</article>
public\home\scripts\comments.js:
const loadCommentsBtnElement = document.getElementById('load-comments-btn');
async function fetchCommentsForPost() {
const postId = loadCommentsBtnElement.dataset.postid;
const response = await fetch(`/blog/${postId}/comments`);
const responseData = await response.json();
console.log(responseData);
}
loadCommentsBtnElement.addEventListener('click', fetchCommentsForPost);
I have noticed that when I remove the code logic for displaying comments on "post-detail.ejs", the page displays and the form for adding comments as well.
Edit: I added console.error(error) before res.status(500).render('500') and I get the following error message:
ReferenceError:
C:\Users\DELL\Desktop\node-com4muz-filedata-database\views\home\post-detail.ejs:30
28|
29|
30| <% if (!comments) { %>
31|
32| This post might have comments. You can load them if you want to view
33| them.
comments is not defined
at eval ("C:\Users\DELL\Desktop\node-com4muz-filedata-database\views\home\post-detail.ejs":40:8)
at post-detail (C:\Users\DELL\Desktop\node-com4muz-filedata-database\node_modules\ejs\lib\ejs.js:703:17)
at tryHandleCache (C:\Users\DELL\Desktop\node-com4muz-filedata-database\node_modules\ejs\lib\ejs.js:274:36)
at View.exports.renderFile [as engine] (C:\Users\DELL\Desktop\node-com4muz-filedata-database\node_modules\ejs\lib\ejs.js:491:10)
at View.render (C:\Users\DELL\Desktop\node-com4muz-filedata-database\node_modules\express\lib\view.js:135:8)
at tryRender (C:\Users\DELL\Desktop\node-com4muz-filedata-database\node_modules\express\lib\application.js:657:10)
at Function.render (C:\Users\DELL\Desktop\node-com4muz-filedata-database\node_modules\express\lib\application.js:609:3)
at ServerResponse.render (C:\Users\DELL\Desktop\node-com4muz-filedata-database\node_modules\express\lib\response.js:1039:7)
at C:\Users\DELL\Desktop\node-com4muz-filedata-database\routes\home\posts.js:49:7
at processTicksAndRejections (node:internal/process/task_queues:96:5) { path:
'C:\Users\DELL\Desktop\node-com4muz-filedata-database\views\home\post-detail.ejs'
}
I fixed the issue.
On views\home\post-detail.ejs on line 49, I replaced:
res.render('post-detail', { post: post });
by:
res.render('post-detail', { post: post, comments: null });

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

Getting a "Cannot read property of undefined" error

I'm trying to retrieve a random value (id) in a dynamic way to as to be able to find a specific product in the future (I'm following a nodejs course from udemy and the instructor is building an online commerce webpage as an example). I've reviewed the code the instructor has written on the video over and over to make sure the error's not on the syntaxis end, it clearly isn't since all's written exactly as it shows in the videos yet I keep on getting the error, if anyone can help please! I've got no clue what it could be since at this point I understand the error could be on the logic end of it, I don't know if maybe I'm not understanding the logic well and therefore the mistake i'm making isn't obvious to me yet.
I'll give the community some of the code so you guys can give me a hdn, thank you all !
Error thrown in console:
TypeError: Cannot read property 'productId' of undefined
at exports.getProduct (C:\Users\TOMAS\Desktop\node js MVC\controllers\shop.js:14:29)
at Layer.handle [as handle_request] (C:\Users\TOMAS\Desktop\node js MVC\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\TOMAS\Desktop\node js MVC\node_modules\express\lib\router\route.js:137:13)
at Route.dispatch (C:\Users\TOMAS\Desktop\node js MVC\node_modules\express\lib\router\route.js:112:3)
at Layer.handle [as handle_request] (C:\Users\TOMAS\Desktop\node js MVC\node_modules\express\lib\router\layer.js:95:5)
at C:\Users\TOMAS\Desktop\node js MVC\node_modules\express\lib\router\index.js:281:22
at param (C:\Users\TOMAS\Desktop\node js MVC\node_modules\express\lib\router\index.js:354:14)
at param (C:\Users\TOMAS\Desktop\node js MVC\node_modules\express\lib\router\index.js:365:14)
at Function.process_params (C:\Users\TOMAS\Desktop\node js MVC\node_modules\express\lib\router\index.js:410:3)
at next (C:\Users\TOMAS\Desktop\node js MVC\node_modules\express\lib\router\index.js:275:10)
product.js file:
const fs = require('fs');
const path = require('path');
const p = path.join(
path.dirname(process.mainModule.filename),
'data',
'products.json'
);
const getProductsFromFile = cb =>{
fs.readFile(p, (err, fileContent) => {
if(err) {
cb([]);
} else{
cb(JSON.parse(fileContent));
}
});
}
module.exports = class Product {
constructor(title, imageUrl, description, price) {
this.title = title;
this.imageUrl = imageUrl;
this.description = description;
this.price = price;
}
save() {
this.id = Math.random().toString();
getProductsFromFile(products => {
products.push(this);
fs.writeFile(p, JSON.stringify(products), (err) => {
console.log(err);
});
});
}
static fetchAll(cb) {
getProductsFromFile(cb);
};
};
shop.js file:
const Product = require('../models/product');
exports.getProducts = (req, res, next) => {
Product.fetchAll(products => {
res.render('shop/product-list', {
prods: products,
pageTitle: 'All Products',
path: '/products'
});
});
};
exports.getProduct = (res, req, next) => {
const prodId = req.params.productId;
console.log(prodId);
res.redirect('/');
};
exports.getIndex = (req, res, next) => {
Product.fetchAll(products => {
res.render('shop/index', {
prods: products,
pageTitle: 'Shop',
path: '/'
});
});
};
exports.getCart = (req, res, next) => {
res.render('shop/cart', {
path: '/cart',
pageTitle: 'Your Cart'
});
};
exports.getOrders = (req, res, next) => {
res.render('shop/orders', {
path: '/orders',
pageTitle: 'Your Orders'
});
};
exports.getCheckout = (req, res, next) => {
res.render('shop/checkout', {
path: '/checkout',
pageTitle: 'Checkout'
});
};
product-list.ejs file:
<%- include('../includes/head.ejs') %>
<link rel="stylesheet" href="/css/products.css">
</head>
<body>
<%- include('../includes/navigation.ejs') %>
<main>
<% if (prods.length > 0) {%>
<div class="grid">
<div class="card">
<% for (let product of prods) { %>
<article class="product-item">
<header class="card__header">
<h1 class="product__title"> <%= product.title %> </h1>
</header>
<div class="card__image">
<img src="<%= product.imageUrl %>", alt="">
</div>
<div class="card__content">
<h2 class="product__price"> $<%= product.price %> </h2>
<p class="product__description"> <%= product.description %> </p>
</div>
<div class="card__actions">
Details
<form action="/add-to-cart" method="POST">
<button class="btn"> Add to Cart </button>
</form>
</div>
</article>
<% } %>
</div>
</div>
<% } else { %>
<h1>No Products</h1>
<% } %>
</main>
<%- include('../includes/end.ejs') %>
users.js file:
const path = require('path');
const express = require('express');
const shopController = require('../controllers/shop');
const router = express.Router();
router.get('/', shopController.getIndex);
router.get('/products', shopController.getProducts);
router.get('/products/:productId', shopController.getProduct);
router.get('/cart', shopController.getCart);
router.get('/orders', shopController.getOrders);
router.get('/checkout', shopController.getCheckout);
module.exports = router;
In shop.js, you need to swap your req and res, your request comes first, then response, then next.
exports.getProduct = (req, res, next) => {
const prodId = req.params.productId;
console.log(prodId);
res.redirect('/');
};
base of
at exports.getProduct (C:\Users\TOMAS\Desktop\node js MVC\controllers\shop.js:14:29)
you have error on shop.js line 14. means :
const prodId = req.params.productId;
req or params is undefined and
TypeError: Cannot read property 'productId' of undefined

Why doesn't ejs template engine find my articles?

I write a simple blog. I can save to and edit an article in my blog, but after saving and editing I can't redirect to the admin page. Also, I have an error from my ejs template engine, because of this: I must refresh again to log in to see an admin_page
This is my routes code:
const express = require('express')
const Article = require('../models/article')
const article = require('../models/article')
const router = express.Router()
router.get('/new', (req, res) => {
res.render('admin/article/new', { article: new Article() })
})
router.get('/edit/:id', async(req, res) => {
const article = await Article.findById(req.params.id)
res.render('admin/article/edit', { article: article })
})
router.get('/:slug', async(req, res) => {
const article = await Article.findOne({ slug: req.params.slug })
if (article == null) {
res.redirect('/')
} else {
res.render('admin/article/show', { article: article })
}
})
router.post('/', async(req, res, next) => {
req.article = new Article()
next()
}, saveArticleAndRedirect('new'))
router.put('/:id', async(req, res, next) => {
req.article = await Article.findById(req.params.id)
next()
}, saveArticleAndRedirect('edit'))
router.delete('/:id', async(req, res) => {
await Article.findByIdAndDelete(req.params.id)
res.render('admin/article/index_admin', { articles: articlee() })
})
function articlee() {
return Article.find().sort({ createdAt: 'desc' })
}
function saveArticleAndRedirect(path) {
return async(req, res) => {
const article = req.article
article.title = req.body.title
article.description = req.body.description
article.markdown = req.body.markdown
try {
article = await article.save().then(() => {
res.render('admin/article/index_admin', { articles: articlee() })
// res.redirect('/admin/article/index_admin')
})
} catch (e) {
res.render(`admin/article/${path}`)
}
// function articlee() {
// return Article.find().sort({ createdAt: 'desc' })
}
}
module.exports = router
And this is the admin page code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Blog</title>
</head>
<body>
<div class="container">
<h1 class="mb-4">Blog Article</h1>
New Article
<% articles.forEach ( article => { %>
<div class="card mt-4">
<div class="card-body">
<h4 class="card-title">
<%= article.title %>
</h4>
<div class="card-subtitle text-muted mb-2">
<%= article.createdAt.toLocaleDateString() %>
</div>
<div class="card-text mb-2">
<%=article.description %>
</div>
Read More
Edit
<form action="/admin/article/<%= article.id %>?_method=DELETE" method="POST" class="d-inline">
<button type="submit" class="btn btn-danger ">DELETE</button>
</form>
</div>
</div>
<% }) %>
</div>
</body>
</html>

Node/Express Routing issue - invoking incorrect path

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.

Categories

Resources