I have an issue that I've been trying to figure out for some time now, and was hoping someone may be able to point me in the right direction.
My variable (error) that I'm passing along in the res.render{} object, is unusable within my layouts file. The issue is logging as a reference error.
If I take the ejs code out, my error properly logs to the terminal; I'm just unable to use it within my layout file.
Following is the layout.ejs code, in part.
<% for(var i = 0; i < errors.length - 1; i++){ %>
<li> <%= errors[i] %> </li>
<% } %>
and POST...
//POST route
app.post('/articles/add', function(req, res){
req.assert('title', 'Enter title').notEmpty();
req.assert('author', 'Enter author').notEmpty();
req.assert('body', 'Enter an article').notEmpty();
//get errors
req.getValidationResult().then(function(err){
if(err.isEmpty()){
console.log(err);
res.render('add_article',{
title: 'Add Article',
errors: err // <-
});
}
else {
let article = new Article();
article.title = req.body.title;
article.author = req.body.author;
article.body = req.body.body;
article.save(function(e){
if(e) {console.log(e)}
else{
req.flash('success', 'Article Added');
res.redirect('/');
}
});
}
});
Thanks for any help.
As per I see there are two bugs within your code. First, the if(err.isEmpty()), when err is empty then you are trying to send err!! And another is use of req.getValidationResult(), it will resolve with result object not an array. Below is the code that might help.
//POST route
app.post('/articles/add', function(req, res){
req.assert('title', 'Enter title').notEmpty();
req.assert('author', 'Enter author').notEmpty();
req.assert('body', 'Enter an article').notEmpty();
//get errors
req.getValidationResult().then(function(result){
if(!err.isEmpty()){
console.log(err);
res.render('add_article',{
title: 'Add Article',
errors: result.array() // <-
});
}
else {
let article = new Article();
article.title = req.body.title;
article.author = req.body.author;
article.body = req.body.body;
article.save(function(e){
if(e) {console.log(e)}
else{
req.flash('success', 'Article Added');
res.redirect('/');
}
});
}
});
And the result.array() will produce something like this:
[
{param: "email", msg: "required", value: "<received input>"},
{param: "email", msg: "valid email required", value: "<received input>"},
{param: "password", msg: "6 to 20 characters required", value: "<received input>"}
]
Related
Please forgive me if i posted it in bad way, but i dont have any idea where is the mistake.
Im doing a small chat with five rooms using Flask-Socket and SocketIO. I set rooms like this:
ROOMS = ["waiting room", "food", "news", "games", "coding"]
Then i put them to html:
#app.route("/chat", methods=['GET', 'POST'])
#login_required
def chat():
return render_template('chat.html', username=current_user.username, rooms=ROOMS)
And on template:
{%for room in rooms%}
<p class="select-room"> {{ room }}</p>
{% endfor %}
The problem is when i try to send message only to room where i am by:
#socketio.on('message')
def message(data):
time_stamp = time.strftime('%b-%d %I:%M%p', time.localtime())
send({'msg': data['msg'], 'username': data['username'], 'time_stamp':time_stamp}, room=data['room'])
and from client:
document.querySelector('#send-message').onclick = () =>{
socket.send({'msg': document.querySelector('#user_message').value,
'username': username, 'room': room});
}
It makes me key error:
send({'msg': data['msg'], 'username': data['username'], 'time_stamp':time_stamp}, room=data['room'])
KeyError: 'room'
it also happens when i trying to switch rooms(leave one and connect other using this):
function leaveRoom(room) {
socket.emit('leave', {'username': username, 'room': room});
document.querySelectorAll('.select-room').forEach(p => {
p.style.color = "black";
});
}
function joinRoom(room) {
socket.emit('join', {'username' : username, 'room' : room});
// Clear message area
document.querySelector('#display-message-section').innerHTML = '';
};
For room select i use:
document.querySelectorAll('.select-room').forEach(p => {
p.onclick = () => {
let newRoom = p.innerHTML
// Check if user already in the room
if (newRoom == room) {
msg = `You are already in ${room} room.`;
printSysMsg(msg);
} else {
leaveRoom(room);
joinRoom(newRoom);
room = newRoom;
};
};
});
Where im making a mistake?
The full code is here:
https://pastebin.com/QXiBQG2u
https://pastebin.com/zfEtV4ZX
https://pastebin.com/wfkqNjf9
//Comment edit route
router.get("/campgrounds/:id/comments/:comment_id/edit", function(req,res){
Comment.findById(req.params.comment_id , function(err,foundComment){
if(err){
res.redirect("back");
} else{
res.render("comments/edit.ejs",{campground_id :req.params.id ,
comment:foundComment});
}
});
});
//Comment update route
router.put("/campgrounds/:id/comments/:comment_id",function(req,res){
Comment.findByIdAndUpdate( req.params.comment_id, req.body.comment ,
function(err,updatedComment){
if(err){
res.redirect("back");
}else{
res.redirect("/campgrounds/" + req.params.id);
}
The ejs file is
<form action="/campgrounds/<%= campground_id %>/comments/<%=comment._id%>?
_method=PUT" method="POST">
<div class="container">
<input type="text" name="comment[text]" value="<%=comment.text%>">
<button>Submit</button>
</div>
</form>
I am not able to edit the comments and update it either, Thanks in advance :)
And here is the comment model code
var mongoose= require("mongoose");
var commentSchema=new mongoose.Schema(
{ text: String,
author: { id: { type:mongoose.Schema.Types.ObjectId, ref: "User" },
username: String }
});
module.exports=mongoose.model("Comment",commentSchema);
I think you need to add some spaces from:
<%=comment.text%>
to
<%= comment.text %>
I keep encountering "ReferenceError: next is not defined" whenever i try to access /catalog/bookinstance/:id/delete. I Spent almost 2 hours trying to troubleshoot but to no avail :(
Any suggestions on which part went wrong?
catalog.js
/* GET request to delete BookInstance. */
router.get('/bookinstance/:id/delete', book_instance_controller.bookinstance_delete_get);
// POST request to delete BookInstance
router.post('/bookinstance/:id/delete', book_instance_controller.bookinstance_delete_post);
bookinstanceController.js
var BookInstance = require('../models/bookinstance');
var Book = require('../models/book');
var async = require('async');
// Display BookInstance delete form on GET
exports.bookinstance_delete_get = function(req, res, next) {
BookInstance.findById(req.params.id)
.populate('book')
.exec(function(err, bookinstance){
if(err){
return next(err);
}
res.render('bookinstance_delete', {title: 'Delete BookInstance', bookinstance: bookinstance})
});
};
// Handle BookInstance delete on POST
exports.bookinstance_delete_post = function(req, res, next) {
//req.checkBody('bookid', 'Book ID must exist').notEmpty();
BookInstance.findByIdAndRemove(req.body.id, function deleteBookInstance(err){
if(err){
return next(err);
}
res.redirect('/catalog/bookinstances');
});
};
bookinstance_delete.pug
h1= title
p #[strong Do you really want to delete this BookInstance?]
div(style="margin-left:20px")
p #[strong ID]: #{bookinstance._id}
p #[strong Title:]
a(href=bookinstance.book.url) #{bookinstance.book.title}
p #[strong Imprint:] #{bookinstance.imprint}
p #[strong Status:]
if bookinstance.status=='Available'
span.text-success #{bookinstance.status}
else if bookinstance.status=='Maintenance'
span.text-danger #{bookinstance.status}
else
span.text-warning #{bookinstance.status}
if bookinstance.status!='Available'
p #[strong Due back:] #{bookinstance.due_back_formatted}
form(method='POST' action='')
div.form-group
input#id.form-control(type='hidden',name='id', required='true', value=bookinstance._id )
button.btn.btn-primary(type='submit') Delete
bookinstance.js
var mongoose = require('mongoose');
var moment = require('moment');
var Schema = mongoose.Schema;
var BookInstanceSchema = Schema({
book: {type: Schema.ObjectId, ref: 'Book', required: true},
imprint: {type: String, required: true},
status: {type: String, required: true, enum: ['Available', 'Maintenance', 'Loaned', 'Reserved'], default: 'Maintenance'},
due_back: {type: Date, default: Date.now},
});
BookInstanceSchema
.virtual('url')
.get(function(){
return '/catalog/bookinstance/' + this._id;
});
BookInstanceSchema
.virtual('due_back_formatted')
.get(function(){
return moment(this.due_back).format('MMMM Do, YYYY');
});
module.exports = mongoose.model('BookInstance', BookInstanceSchema);
I have a simple contact form using the Node Sendgrid helper library.
I would like to use a template email/contact.jade that compiles to HTML and adds the right context. I know it needs to go in the payload.html value, however I'm stuck on how to send the email with the template.
routes.js
app.route('/contact')
.post(function(req, res) {
var template = 'email/contact.jade';
var payload = {
to: req.body.email,
from: 'example#example.com',
subject: req.body.subject,
html: req.body.message
};
var email = new sendgridClient.Email(payload);
sendgridClient.send(email, function(err, json) {
if (err) {
return console.error(err);
} else {
res.redirect('/thanks');
}
});
});
email/contact.jade
p Thanks
p Name {{ name }}
p Email {{ email }}
p Subject {{ subject }}
p Message {{ message }}
First I'm not sure your jade syntax is correct. You could try this instead:
email/contact.jade
p Thanks
p Name #{name}
p Email #{email}
p Subject #{subject}
p Message #{message}
And to render this into HTML:
var jade = require('jade');
var templatePath = __dirname + '/contact.jade';
app.route('/contact')
.post(function(req, res) {
var payload = {
to: req.body.email,
from: 'example#example.com',
subject: req.body.subject,
html: jade.renderFile(templatePath, req.body)
};
//...
});
I'm having some trouble getting this table to load properly because the page is loading before all the information is passed to my ejs template. Pretty new to all of this and would appreciate any help!
I should note that owneditems is an array of IDs in the user schema.
routes.js:
app.get('/profile/:username', function(req, res) {
User.findOne({username: req.params.username}, function(err, user) {
var newDocs = [];
if (!user) {
req.flash('profilemessage', 'No such user exists.');
} else {
user.owneditems.map(function(i) {
Items.findById(mongoose.Types.ObjectId(i), function(err, idoc) {
newDocs.push("<tr><td>" + idoc.name + "</td><td>" + idoc.brand</td></tr>");
});
});
}
res.render('profile.ejs', {title: 'Profile', items: newDocs, message: req.flash('profilemessage')});
});
});
Profile.ejs:
<!-- content -->
<div class="wrapper row2">
<div id="container" class="clear">
<section>
<% if (message) { %>
<h4><%= message %></h4>
<% } %>
<table id="owneditems" class="sortable">
<tr><th>Name</th><th>Brand</th></tr>
<% for(var i=0; i<items.length; i++) {%>
<%- items[i] %>
<% } %>
</table>
</section>
</div>
</div>
<% include layoutBottom %>
This type of setup works for me on another page, I just can't get it working here. Thanks!
The reason why the page is rendered before information is loaded is becauseItems.findById is asynchronous. This means newDocs will not return the array of items you're expecting when it's passed to res.render.
When you want to load (arrays of) subdocuments with Mongoose, it's best to use query#populate. This method will allow you to swap out the item IDs in your user.owneditems array for the actual item document in one go.
I think this would work in your case:
app.get('/profile/:username', function(req, res) {
User.findOne({username: req.params.username})
.populate('owneditems')
.exec(function(err, user) {
var newDocs = [];
if (!user) {
req.flash('profilemessage', 'No such user exists.');
} else {
user.owneditems.forEach(function(i) {
newDocs.push("<tr><td>" + i.name + "</td><td>" + i.brand</td></tr>");
});
}
res.render('profile.ejs', {title: 'Profile', items: newDocs, message: req.flash('profilemessage')});
});
});
Also note I switched map with forEach (which is what it seems you're going for given your callback)