showing MongoDB document value to EJS in a loop - node.js

I'm working on a node project. And node is very new to me so sorry if I ask a lot of stupid questions.
I'm trying to show all of my topics on my index.ejs. The root file already shows them in the console log but I can't seem to get them to show in the view.
The view already loops and knows that there are 2 topics, but the content is empty.
This is my code from the routes/index.js
router.get('/', function(req, res, next) {
Topic.find({}).exec(function(err, topic)
{
if(err){
console.log('There ware no topics');
return next(err)
}
else
{
console.log('Whoop whoop there are some topics');
res.render('index', { topic: topic } );
console.log("Logging data: " + topic);
console.log("Loggin data title out db: " + topic.topicTitle);
console.log("Loggin data desc out db: " + topic.topicDescription);
console.log("Loggin data date out db: " + topic.topicDateCreated);
}
});
});
module.exports = router;
And here is how I try to show it in my views/index.ejs
<ul>
<% for(var i = 0; i < topic.length; i++) {%>
<li>
<div class="post animated fadeInLeft">
<div class="wrap-ut pull-left">
<div class="userinfo pull-left">
<div class="avatar">
<img src="images/avatar.jpg" alt="default avatar" />
<p class="moderator"> <%= topic.fbUsername %> </p>
</div>
</div>
<div class="posttext pull-left">
<h2 class="topictitle"><a href="/topicdetail/{topic_id}" <%= topic.topicTitle %> </a></h2>
</div>
<div class="clearfix"></div>
</div>
<div class="clearfix"></div>
</div>
</li>
<% } %>
</ul>

The topic variable is an array, so you need to pass the index in order to get the proper Topic document. For instance:
<%= topic[i].fbUsername %>
Your view should look like:
<ul>
<% for(var i = 0; i < topic.length; i++) {%>
<li>
<div class="post animated fadeInLeft">
<div class="wrap-ut pull-left">
<div class="userinfo pull-left">
<div class="avatar">
<img src="images/avatar.jpg" alt="default avatar" />
<p class="moderator"> <%= topic[i].fbUsername %> </p>
</div>
</div>
<div class="posttext pull-left">
<h2 class="topictitle"> <%= topic[i].topicTitle %> </h2>
</div>
<div class="clearfix"></div>
</div>
<div class="clearfix"></div>
</div>
</li>
<% } %>
</ul>
UPDATE
You also forgot to close the tag here:
--------------------------------------------------------↴
<h2 class="topictitle"><a href="/topicdetail/{topic_id}" <%= topic[i].topicTitle %> </a></h2>
Closing the tag, I was able to see the titles:
<h2 class="topictitle"> <%= topic[i].topicTitle %> </h2>

Related

error in nodejs using sripe for payment for website

I am implementing a payment method in my e-commerce website using stripe and node.js , and I am not using a database only using JSON files to add my items to the website, but I am getting errors in ejs file. for rendering the page I used the fs module in node js. I wanted to know where I can define the items in ejs , or server , or HTML file
''' shop.ejs'''
<div class="box-container">
<% items.products.forEach(function(item){ %>
<div class="box" data-item-id="<%= item.id %>">
<div class="icons">
</div>
<img class="image" src="/image/<%= item.imgName %>" alt="">
<div class="content">
<div class="price"> &#8377 <%= item.price %></div>
<h3 class="shop-item-title"><%= item.name %></h3>
<div class="stars">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="far fa-star"></i>
<span> (50) </span>
</div>
</div>
</div>
<% }) %>
'''server.js'''
app.get('/shop', function(req, res){
fs.readFile('items.json', function(error, data){
if(error){
res.status(500).end()
}else {
res.render('shop.ejs', {
items: JSON.parse(data)
})
}
})
})
'''error'''
>> 172| <% items.products.forEach(function(item){ %>
173| <div class="box-container" data-item-id="<%= item.id %>">
174| <div class="box">
175| <div class="icons">
items are not defined
Did you pass the items data object to the ejs render function?

Node w/ express using ejs: "ReferenceError: plants is not defined"

I'm getting this error in my when I load my .ejs view:
ReferenceError: plants is not defined
All of my other views work with the includes and such.
plants.js router:
router.get('/plants', plantsController.getPlantsPage);
plants.js controller:
const Plant = require('../models/plant');
exports.getPlantsPage = (req, res, next) => {
res.render('plants', {
plants: plants,
pageTitle: 'Plants',
path: '/plants',
hasPlants: plants.length > 0
});
};
plant.js model:
module.exports = class Plant {
constructor(common_name, image_url, scientific_name) {
this.common_name = common_name;
this.image_url = image_url;
this.scientific_name = scientific_name;
}
}
plants.ejs:
<% if (hasPlants) { %>
<% for (let plant of plants) { %>
<div class="col s12 m4">
<div class="card">
<div class="card-image waves-effect waves-block waves-light">
<img class="activator" src="<%= plant.image_url %>">
</div>
<div class="card-content">
<span class="card-title activator grey-text text-darken-4"><%= plant.common_name %><i class="material-icons right">MORE</i></span>
</div>
<div class="card-reveal">
<span class="card-title grey-text text-darken-4"><%= plant.common_name %><i class="material-icons right">CLOSE</i></span>
<p><%= plant.scientific_name %></p>
<p>Plant Details</p>
</div>
</div>
</div>
<% } %>
<% } else { %>
<div class="row">
<div class="col s12">
<p>No Plants Added!</p>
</div>
</div>
<% } %>
Thanks.
Did you make sure you set the plants variable? The error says plants is not defined. Try console.log() the variable.

Error: Could not find matching close tag for "<%"

<% if (users.length > 0) { %>
<div class="grid">
<% for(user of users) { %>
<article class="card product-item">
<header class="card__header">
<h1 class="product__title">user.Username</h1>
</header>
<div class="card__actions">
<button class="btn">Go to profile</button>
</div>
</article>
<% } %>
</div>
<% } else { %>
<h3>No users are registered yet!</h3>
<% } %>
Inside the if statement > (greater than) sign is recognizing as closing tag. What should I do?
There are no syntax errors in the code supplied when tested using the following code (question code is contained in the template variable):
const ejs = require('ejs');
let template = `
<% if (users.length > 0) { %>
<div class="grid">
<% for(user of users) { %>
<article class="card product-item">
<header class="card__header">
<h1 class="product__title">user.Username</h1>
</header>
<div class="card__actions">
<button class="btn">Go to profile</button>
</div>
</article>
<% } %>
</div>
<% } else { %>
<h3>No users are registered yet!</h3>
<% } %>
`;
const renderData = {
users: [{
Username: 'test'
}]
};
const output = ejs.render(template, renderData);
console.log(output);
Console output:
<div class="grid">
<article class="card product-item">
<header class="card__header">
<h1 class="product__title">user.Username</h1>
</header>
<div class="card__actions">
<button class="btn">Go to profile</button>
</div>
</article>
</div>
However, as noted by #eol, user.Username should be wrapped in output tags.

forEach loop with if and else statements

So the title might not be the best description of my problem because I don't really know how to sum up the problem. I have a forEach loop for each event in my database and each one is classified as type = to either "trip", "ropes course", or "other". I have a section for each type of event, but I would like a way where if there are no events in the section I can not display the bannertron header and if there are no events in the whole database I can display different HTML. My code also seems very WET and I would love any suggestions on how to DRY up this page.
<% include partials/header %>
<div class="image-text">
<img src="../images/trip-photo.jpg" class="bannertron">
<div class="centered">Upcoming Trips</div>
</div>
<div class="container event">
<% events.forEach(function(event){ %>
<% if(event.type === "trip"){ %>
</br>
<h4><strong><%= event.title %></strong></h4>
<span><%= event.startdate %> - </span>
<span><%= event.enddate %></span>
<h6><strong>Location: </strong><%= event.location %></h6>
<p><%= event.description %></p>
<% if(currentUser && (currentUser.admin === true)){ %>
Edit
<form action="/calendar/<%= event._id %>?_method=DELETE" method="POST">
<button class="btn btn-danger">Delete</button>
</form>
<% } %>
<hr class="event-hr">
<% } %>
<% }); %>
</div>
<div class="image-text">
<img src="../images/climbing-photo.jpg" class="bannertron">
<div class="centered">Upcoming Climbing Days</div>
</div>
<div class="container event">
<% events.forEach(function(event){ %>
<% if(event.type === "ropescourse"){ %>
</br>
<h4><strong><%= event.title %></strong></h4>
<span><%= event.startdate %> - </span>
<span><%= event.enddate %></span>
<h6><strong>Location: </strong><%= event.location %></h6>
<p><%= event.description %></p>
<% if(currentUser && (currentUser.admin === true)){ %>
Edit
<form action="/calendar/<%= event._id %>?_method=DELETE" method="POST">
<button class="btn btn-danger">Delete</button>
</form>
<% } %>
<hr class="event-hr">
<% } %>
<% }); %>
</div>
<div class="image-text">
<img src="../images/other-photo.jpg" class="bannertron">
<div class="centered">Other Events</div>
</div>
<div class="container event">
<% events.forEach(function(event){ %>
<% if(event.type === "other"){ %>
<h4><strong><%= event.title %></strong></h4>
<span><%= event.startdate %> - </span>
<span><%= event.enddate %></span>
<h6><strong>Location: </strong><%= event.location %></h6>
<p><%= event.description %></p>
<% if(currentUser && (currentUser.admin === true)){ %>
Edit
<form action="/calendar/<%= event._id %>?_method=DELETE" method="POST">
<button class="btn btn-danger">Delete</button>
</form>
<% } %>
<hr class="event-hr">
</br>
<% } %>
<% }); %>
</div>
<% include partials/footer %>
Ideally, I would be able to loop through the events, check which type it is, put it in its respective area, and if there are none of a type, display some text like "no events scheduled" and if there are no events in total, display something else.
If this was me, I would split the events up into 3 different arrays in the node file, instead of sorting it all out inside of an ejs file. The Array.prototype.filter command would be useful for this. The nice thing about it is it returns a new array, with only items that return true for the function you pass in. It does not alter the original array, just passes a new one in. You can do something like
var trips = events.filter(function(event){
return event.type == "trip";
});
//do similar for "rope course", and for "other" you could do something like
return event.type != "trip" && event.type != "rope course"
// for the return statement.
You can do a
if(events.length == 0){
// show different html file
} else {
// show the html file with events, passing in the arrays
}
Make sure you pass all the arrays into the ejs file. For each array, you can do a similar
if(trips.length != 0){
// show banner and stuff
}
And do it for all three.
Hope this helps!

UnhandledPromiseRejectionWarning

Reference block of code below:
//create comment route: post new comment to database
router.post("/campgrounds/:id/comments", isLoggedIn, function (req,res){
//lookup campground using ID
Campground.findById(req.params.id, function(err,campground){
if(err){
console.log(err);
res.redirect("/campgrounds");
} else {
Comment.create(req.body.comment, function(err, comment){
if(err){
console.log("Error Occured");
console.log(err);
} else {
//add username and id to comment
console.log("New Comment User Name will be " + req.user.username);
comment.author.id = req.user._id;
comment.author.username = req.user.username;
//save comment
comment.save();
campground.comments.push(comment);
campground.save();
console.log(comment);
res.redirect("/campgrounds/" + campground._id);
}
})
}
});
});
I realized that if I emptied the database, created a new campground, adding the first comment to that campground is always successful and is displayed in the ejs html template at the "res.redirect..." statement. However, subsequent comment addition generates the error/warning below:
(node:1400) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): ValidationError: Campground validation failed: comments: Cast to [undefined] failed for value "[{"author":{"id":"5a800e7d0646ee0837c2787e","username":"success"},"_id":"5a84b148a953bc03b4f58180","text":"Nice camp in a wooden environment... Yayy! I will be visiting this place next week","__v":0}]" at path "comments
Despite the warning/error above the code still executes to the line;
res.redirect("/campgrounds/" + campground._id);
However the ejs html template at /campgrounds/" + campground._id which contains a routine to loop through the comments on a particular campgrounds and display all of them, only shows the first comment, even though there are multiple comments against the campground.
below is the ejs template:
<% include ../partials/header %>
<div class=container>
<div class = "row">
<div class="col-md-3">
<p class="lead">YelpCamp</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="thumbnail border">
<img class="img-responsive" src=" <%= campground.image %> " alt="">
<div class="caption-full">
<h4 class="float-right">$9.00/night</h4>
<h4><%=campground.name%></h4>
<p class="text-justify">
<%= campground.description %>
</p>
</div>
</div>
<p></p>
<div class="card bg-light">
<div class ="card card-header">
<div class = "text-right">
<a class="btn btn-success" href="/campgrounds/<%= campground._id %>/comments/new">Add new Comment</a>
</div>
<p></p>
</div>
<% campground.comments.forEach(function(comment){ %>
<div class="row">
<div class="col-md-12">
<strong> <%= comment.author.username %> </strong>
<span class="float-right">10 days ago</span>
<p>
<%= comment.text %>
</p>
</div>
<hr>
</div>
<% }); %>
</div>
</div>
</div>
</div>
<% include ../partials/footer %>
Updating the mongoose version to 5.0.6 from 5.0.1 cleared the warning.

Resources