CKEditor, MongoDB, and NodeJs: Text is displayed in HTML Format - javascript

I'm currently building my first blog site where I'm using CKEditor v4.14.1 to allow the user to make a formatted blog entry. However, the user input is showing on the webpage as HTML instead of formatted text. I'm using MongoDB to store the blog entry as well.
This is the user input:
And this is the output on the webpage:
new.ejs
<form action="/posts" method="POST" enctype="multipart/form-data">
<div class="input-area">
<textarea name="post[body]" id="editor" placeholder="Body"></textarea>
</div>
<input type="submit" id="submit">
</form>
<script>
CKEDITOR.replace('editor')
</script>
MongoDB Post Schema
const PostSchema = new Schema({
body: String
})
PostSchema.plugin(mongoosePaginate)
module.exports = mongoose.model('Post', PostSchema);
controllers/posts.js
const Post = require('../models/post');
// Posts Create
async postCreate(req, res, next) {
let post = new Post(req.body.post);
post.body = req.body.post.body;
await post.save();
}
show.ejs
<div><%= post.body %></div>

My show.ejs should be
<div><%- post.body %></div>
instead of
<div><%= post.body %></div>

Related

How can I get the items in this JSON file to display properly?

So I am not sure what I am doing wrong here. I have these three items from a service and it is in JSON format. When I write out the loop to just display the artist name and the song I keep getting an error
This is the API command in server.js
app.post('/process_form', function(req, res){
var search = req.body.search
axios.get('https://itunes.apple.com/search?term='+search)
.then((response)=>{
var songlist = response.data.results;
console.log(songlist);
res.render('pages/thanks.ejs', {
songlist: songlist,
search: search
});
});
})
This is the code for the html form where the user can enter the name of a song or artist
<form action="/process_form" method="post">
<h1>Which song would you like to lookup</h1>
<div>
<input type="text" id="search" name="search">
</div>
<input type="submit">
And this is the form where the results will be output in a list
<ul>
<% songlist.forEach(function(songlist) { %>
<li>
<strong><%= songlist[artistName] %></strong>
<strong><%= songlist[TrackName] %></strong>
</li>
<% }); %>
</ul>
This is what the original source of the data looks like.
{
wrapperType: 'track',
kind: 'song',
artistId: 216123617,
collectionId: 982317323,
trackId: 982317327,
artistName: 'Rockabye Baby!',
collectionName: 'Lullaby Renditions of Radiohead',
trackName: 'Let Down',
},
Lastly the error I get is that. I am just confused as to why it is saying that I did not define songList
5| <main>
6| <ul>
>> 7| <% songlist.forEach(function(songlist) { %>
8| <li>
9| <strong><%= songlist[artistName] %></strong>
10| <strong><%= songlist[TrackName] %></strong>
songlist is not defined
Sorry for the long post. I am just super frustrated at this and I do not know what I am doing wrong
EDIT: I am using EJS as the view engine
app.set('view engine', 'ejs');
I found the solution was that when was actually rendering my index file, I put
res.render(index)
When the file name was supposed to be
res.render(index.ejs)

Add Update Functionality

Sorry if this is super obvious, but I'm new to coding. I'm using node.js, express, mongoose and mongodb to try and add an update function to my app to make it CRUD by adding an edit button. Whenever I click the edit button though it still just deletes the item as if I were clicking the checkbox. I'm thinking it's because I'm calling the item from the same form of "pending items" but it seems like my update code isn't even registering as my console.logs for //Edit items aren't logging.
I want it to identify the item by its id when its edit button is submitted (then put the item in the newTask input to be edited and resubmitted as an update but I haven't figured out how to link those 2). I know the code is wonky, I'm just trying to figure out how to put this together, so thanks for any help!
<div class="box">
<!-- Item add function -->
<% newListItems.forEach(function(item){ %>
<!-- Form for pending items -->
<form action="/update" method="post">
<!-- Items -->
<div class="item">
<input type="checkbox" name="checkbox" value="<%=item._id%>" onChange="this.form.submit()">
<button type="submit" class="editItmbtn" name="editItembtn">E</button>
<p><%=item.name%></p>
</div>
<input type="hidden" name="listName" value="<%= listTitle %>"></input>
</form>
<% }) %>
<!-- End pending items -->
<!-- Form to add items -->
<form class="item" action="/" method="post">
<input type="text" name="newTask" id="id" placeholder="Add new task..." autocomplete="off">
<button type="submit" name="list" value="<%= listTitle %>">+</button>
</form>
</div>
''//Requirements
const express = require("express");
const session = require("express-session")//for sessions
const favicon = require("serve-favicon"); //for favicon
const path = require("path");// for favicon
const bodyParser = require("body-parser");
const cookieParser = require("cookie-parser"); //for sessions
const mongoose = require("mongoose"); const _ = require("lodash");
const MongoStore = require("connect-mongo")(session);
const app = express();
// Edit items
app.put("/update", function(req, res) {
const itemName = req.body.newTask;
const taskID = req.body.editItmbtn;
const userInput = req.body.id;
Item.useFindAndModify(taskID), {
$set: {"/update": userInput}}, {new: true},
(err, result) => {
if (err) {
console.log("ERROR");
} else {
res.redirect("/");
res.render("list", {
listTitle: "Tasks",
newListItems: foundItems
});
}
}
});
// Delete checked items
app.post("/update", function(req, res) {
const checkedItemId = req.body.checkbox;
const listName = req.body.listName;
if (listName === "Tasks") {
Item.findByIdAndRemove(checkedItemId, function(err) {
if (!err) {
console.log("Successfully deleted checked item.");
res.redirect("/");
}
});
} else {
List.findOneAndUpdate({
name: listName
}, {
$pull: {
items: {
_id: checkedItemId
}
}
}, function(err, foundList) {
if (!err) {
res.redirect("/" + listName);
}
});
}
});
In your form you are using the method="post" which means that the request will be submitted to your app.post("/update" ..... ) , that is why you always land there, where you delete your item.
You have to do two things:
Change the app.put('/update' ...) to app.post('/update' ...)
Change the app.post('/update' ... which is meant to delete the item to something semantically more relevant like maybe app.post('/delete' ... and change the frontend with which you delete method respectively.
You have app.put("/update", function(req, res) in Express but <form action="/update" method="post"> in your form, which matches your delete route.
For your second question, there are many ways to populate your item form in Javascript, but there might be an even more efficient way if you tell us if you're using a framework.
Your form always submit the http request with the "POST" method, but in your code the app.post("update") deletes the item

Passing an error from POST route to ejs template

So I've been trying to figure out for hours, how to pass an error from a POST route in Express to an EJS template. I'm building a blog app and I want to prevent the redirect to the root route and instead display an error if the title input or text area are empty. Can this be done server-side or do I have to track the inputs on the client-side?
Here is my Compose template:
<form action="/compose" method="POST">
<div class="form-group">
<label for="postTitle">Title</label>
<input type="text" name="postTitle" class="form-control" id="postTitle" autocomplete="off">
<label for="postBody">Post</label>
<textarea name="postBody" class="form-control" autocomplete="off" rows="8"></textarea>
</div>
<button type="submit" name="button" class="btn btn-primary">Publish</button>
</form>
Here's my GET and POST routes:
compose_get: (req, res) => res.render("compose"),
compose_post: (req, res) => {
const postTitle = req.body.postTitle;
const postBody = req.body.postBody;
let postDate = new Date();
const post = new Posts({
date: postDate,
title: postTitle,
content: postBody
});
post.save(err => {
if (!err) {
res.redirect("/");
}
});
}

How to use onchange to render ejs via a select element

I am using Node.js to render EJS files. I want a user to fill out a log form and have their data sent to a database. The initial part of the form is in log.ejs, is rendered via log.js, and is incomplete. When the user selects an option, I want the rest of the form to show up. I want each option to render a slightly different form (i.e. easyForm, longrunForm, etc.) and complete the original form while still using log.ejs. I've tried to accomplish this by adding <%- include('logForms/easyForm.ejs') -%> after the incomplete form in log.ejs and have tried doing this several different ways with the <select> elements onclick="" attribute. I have not come up with functioning code and I do not know the best way to go about this with EJS format. If I manually add <%- include('logForms/easyForm.ejs') -%> after the incomplete form in log.ejs, then the form loads and I can save data successfully to my database. Any help would be appreciated.
log.ejs
<%- include('partials/header.ejs') -%>
<!--Form begins but has no closing -->
<form class="" action="/log" method="post">
<div class="form-group">
<label for="">Date</label>
<input type="date" name="workoutDate" class="">
<label for="">Time</label>
<input type="time" name="timeOfDay" placeholder="Time of day hh:mm">
<select class="" id="workoutSelect" name="workoutType" onchange="">
<option disabled selected value>-- select an option --</option>
<option value="run">Run</option>
<option value="longRun">Long run</option>
<option value="shakeout">Shakeout</option>
<option value="workout">Workout</option>
<option value="race">Race</option>
<option value="xTrain">X-Train</option>
</select>
</div>
<!--This is where specific logForm is used to add appropriate fields and close the form -->
<%- include('partials/footer.ejs') -%>
log.js
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const Data = require('../models/data');
router.get('/', (req, res, next) => {
res.render('log');
});
router.post('/', (req, res, next) => {
const data = new Data ({
date: req.body.workoutDate,
timeOfDay: req.body.timeOfDay,
type: req.body.workoutType,
otherData: req.body.otherData,
});
data.save()
.then(results => {
console.log(results);
res.render('log');
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
module.exports = router;

Display time elapsed since timestamp of a mongodb object + reformatting timestamp

I'm creating a blog and in the blog you can add comments (obviously). In my mongodb schema the comment object is as follows:
var commentSchema = mongoose.Schema({
id: mongoose.Schema.Types.ObjectId,
text: String,
created: {type: Date, default: Date.now},
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String,
image: String
}
});
I'm extracting the timestamp (created) and displaying it when a comment is posted using the following:
<div id="comments">
<% blog.comments.forEach(function(comment){ %>
<div class="jumbotron comment">
<div class="row">
<div class="col-md-1">
<img class="comment-ico" src = "<%=comment.author.image%>">
</div>
<div class="col-md-7">
<h4><%=comment.author.username%></h4>
</div>
<div class="col-md-4 date">
<%= comment.created.toDateString()%>
</div>
</div>
</div>
<div><p><%=comment.text%></p></div>
However, this is just displaying the date in the following format: Fri Mar 24 2017
What I would like to display is a time since comment was posted. For example: "1 min ago", "10 mins ago" etc. How can I use JS to display this?
And on a similar note, if I want to display the date, how can I reformat to mm/dd/yyyy?
Thanks
Update:
Here is my comments create route which is stored in routes/comment.js:
router.post("/", middleware.isLoggedIn, function(req, res){
// lookup blog using id
Blog.findById(req.params.id, function(err, blog){
if(err) {
console.log(err);
res.redirect("/blogs");
} else {
// create new comment
Comment.create(req.body.comment, function(err, comment){
if(err) {
req.flash("error", "Something went wrong");
console.log(err);
} else {
comment.author.id = req.user._id;
comment.author.username = req.user.username;
comment.author.image = req.user.image;
comment.save();
// connect new comment to campground
blog.comments.push(comment);
blog.save();
var commentCreated = comment.created.toDateString();
if(req.xhr){
res.json({comment: comment, commentCreated: commentCreated, blog: blog});
} else {
// // redirect to campground show page
req.flash("success", "Successfully added comment");
res.redirect("/blogs/" + blog._id);
}
}
});
}
});
});
And then I am using AJAX in a separate file (/public/ajax.js) to display asynchronously:
$('#newComment').submit(function(e){
e.preventDefault();
var formData = $(this).serialize();
var formAction = $(this).attr('action');
$.post(formAction, formData, function(data){
console.log(data);
$("#comments").append(
`<div class="jumbotron comment">
<div class="row">
<div class="col-md-1">
<img class="comment-ico" src = "${data.comment.author.image}">
</div>
<div class="col-md-7">
<h4>${data.comment.author.username}</h4>
</div>
<div class="col-md-4 date">
${data.commentCreated}
</div>
</div>
</div>
<div id="A<%=comment._id%>"><p>${data.comment.text}</p></div>
<form id="edit-comment-form" action = "/blogs/data._id %>/comments" method = "POST" id="newComment">
<textarea class = "form-control" rows="4" placeholder = "Type comment here..." name = "comment[text]"></textarea>
<button class = "btn btn-lg btn-primary btn-block">Submit</button>
</form>
<div class="row" id="B${data.comment._id}">
<div class="col-md-1 choice">
<a class="edit">Edit</a>
</div>
<div class="col-md-1 choice1">
<form id = "delete-form" action = "/blogs/${data.blog._id}/comments/${data.comment._id}?_method=DELETE" method = "POST">
<input type = "submit" class = "button-delete" value = "Delete">
</form>
</div>
</div>
<hr class = "style-three">`
);
$('#newComment').find('.form-control').val('');
});
});
Inject a moment object into your ejs templates that manipulates date objects to display different formats. For example:
var moment = require('moment');
var Blog = require('./models/blog');
exports.index = function(req, res) {
Blog.find().exec(function(err, blogs){
if (err) throw err;
// send moment to your ejs
res.render('index', { moment: moment, blogs: blogs });
});
}
And in your template, use the fromNow() API for displaying the timeago or relative time:
<div id="comments">
<% blog.comments.forEach(function(comment){ %>
<div class="jumbotron comment">
<div class="row">
<div class="col-md-1">
<img class="comment-ico" src = "<%=comment.author.image%>">
</div>
<div class="col-md-7">
<h4><%=comment.author.username%></h4>
</div>
<div class="col-md-4 date">
Created <%= moment(comment.created).fromNow(true) %> ago
</div>
<!--<div class="col-md-4 date">
Created at <%= moment(comment.created).format('Do MMM YYYY') %>
</div>-->
</div>
</div>
<div><p><%=comment.text%></p></div>
Another alternative is to create an ejs filter function that will return fromNow:
JavaScript
var ejs = require('ejs');
var moment = require('moment');
ejs.filters.fromNow = function(date) {
return moment(date).fromNow();
}
Template
<div class="col-md-4 date">
Created <%= comment.created | fromNow %> ago
</div>
Remember to have moment added to your package.json file:
npm install moment
UPDATE
Using your actual code, you only need to use the moment object on the line you create the commentCreated variable:
// create new comment
Comment.create(req.body.comment, function(err, comment){
if(err) {
req.flash("error", "Something went wrong");
console.log(err);
} else {
comment.author.id = req.user._id;
comment.author.username = req.user.username;
comment.author.image = req.user.image;
comment.save();
// connect new comment to campground
blog.comments.push(comment);
blog.save();
var commentCreated = moment(comment.created).fromNow(); // use moment here
if(req.xhr){
res.json({comment: comment, commentCreated: commentCreated, blog: blog});
} else {
// // redirect to campground show page
req.flash("success", "Successfully added comment");
res.redirect("/blogs/" + blog._id);
}
}
});

Categories