Node.js: Success and Error message is not showing - javascript

I want to show success message on my single post. I am using connect-flash, express-session to show the flash message.
Tried to flash the message in different page too. But, the message is not casting. So, it's not showing at any pages. I am following the scotch to show the success message.
When i am trying to submit blank the form, it shows the following error in console MongoError: E11000 duplicate key error collection
schema:
const eventSchema = new Schema({
name: {
type: String,
require: [true, 'Event name is required']
},
slug: {
type: String,
unique: true,
sparse: true
},
description: {
type: String,
require: [true, 'Event description is required']
},
}, {collection: 'event'});
const eventModel = mongoose.model('Event', eventSchema);
app.js:
const session = require('express-session'),
flash = require('connect-flash');
app.use(session({
secret: 'secret-key',
cookie: {
secure: true,
maxAge: 60000,
},
resave: false,
saveUninitialized: false
}));
app.use(flash());app.use(session({
secret: 'secret-key',
cookie: {
secure: true,
maxAge: 60000,
},
resave: false,
saveUninitialized: false
}));
app.use(flash());
controller:
const Event = require('../models/event'),
{ body, validationResult } = require('express-validator');
exports.showSingleEvent = async(req, res) => {
try {
await Event.findOne({ slug: req.params.slug }).exec((error, event) => {
if(error) {
res.status(404);
res.send('Events not found');
}
res.render('pages/single', {
title: `Event: ${event.name}`,
event: event,
success: req.flash('success')
});
});
} catch (error) {
return next(error);
}
};
exports.createEvent = ([
// Validate data
body('name')
.isLength({ min: 5, max: 30 })
.not().isEmpty()
.trim()
.escape(),
body('description')
.isLength({ min: 5, max: 200 }).withMessage('Must be between 5 & 200')
.not().isEmpty()
.trim()
.escape()
], async(req, res) => {
// Check if has errors
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
} else {
const event = new Event({
name: req.body.name,
description: req.body.description
});
await event.save((error) => {
if(error) throw error;
// Flash successful message
req.flash('success', 'Data has created successfully!');
// Redirect to newly created event
res.redirect(`/events/${event.slug}`);
});
}
});
single.ejs:
<% if (success.length > 0) { %>
<div class="alert alert-success mt-2 mb-2">
<%= success %>
</div>
<% } %>
<div class="jumbotron text-center">
<h1><%= event.name %></h1>
<p><%= event.description %></p>
<p>
All events
</p>
</div>
create.ejs:
<% if (errors.length > 0) { %>
<div class="alert alert-success mt-2 mb-2">
<% for (error of errors ) { %>
<%= error %> <br>
<% } %>
</div>
<% } %>
<form action="/events/create" method="POST">
<div class="form-group">
<label>Name</label>
<input type="text" name="name" class="form-control">
</div>
<div class="form-group">
<label>Description</label>
<textarea name="description" rows="5" class="form-control"></textarea>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success btn-lg">Create</button>
</div>
</form>

Related

Vue form validation

I have some problems with mine Vue app.
I'm trying to validate login form that is connected with my Laravel App.
This is how template looks
<template>
<div>
<div class="main" v-if="canLogin">
<img class="logo" src="../assets/logo.png">
<form id="app"
#submit="checkForm"
method="post">
<p v-if="validation.length">
<b>Please correct the following error(s):</b>
<ul>
<li v-for="validation in validation">{{ error }}</li>
</ul>
</p>
<input class="form-input" type="email" v-model="form.email" id="email" align="center" placeholder="eMail" required>
<input class="form-input" type="password" v-model="form.password" id="password" align="center" placeholder="Password" required>
<button #click.prevent="login" class="submit">Sign In</button>
</form>
</div>
<div class="main" v-if="!canLogin">
<span> YOU ARE BLOCKED FOR 15 MINUTES</span>
</div>
</div>
</template>
As you see I want to foreach errors, but it's always giving error that
'validation' is defined but never used
And this is how mine script looks.
<script>
import User from "../apis/User";
export default {
data() {
return {
form: {
email: "",
password: ""
},
validation: [],
errors: '',
message: '',
canLogin: true,
};
},
mounted() {
User.canLogin().then(response => {
this.canLogin = response.data.canLogin;
});
},
methods: {
checkForm: function (e) {
this.errors = [];
if (!this.form.password) {
this.errors.push("Name required.");
}
if (!this.form.email) {
this.errors.push('Email required.');
} else if (!this.validEmail(this.email)) {
this.errors.push('Valid email required.');
}
if (!this.errors.length) {
return true;
}
e.preventDefault();
},
validEmail: function (email) {
var re = /^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
},
login() {
User.login(this.form)
.then(response => {
this.$store.commit("LOGIN", true);
localStorage.setItem("token", response.data.token);
this.$router.push({ name: "Dashboard" });
this.$snotify.success(response.data.message, {
timeout: 2000,
showProgressBar: true,
closeOnClick: true,
pauseOnHover: true
});
})
.catch(error => {
if (error.response.status === 400) {
this.errors = error.response.data.message;
this.$snotify.error(error.response.data.message, {
timeout: 2000,
showProgressBar: true,
closeOnClick: true,
pauseOnHover: true
});
}
if(error.response.status === 429){
this.canLogin = false;
}
});
}
}
};
</script>
I'm catching few thhings, like, canLogin, this is checking if IP is not blocked.
There is one more error like:
Elements in iteration expect to have 'v-bind:key' directives
I'm just a started with vue so don't judge me if it's simple fix.
BTW: without validation works fine, I believe it's not only problem with those errors and probbly I'm not catching some things as needed.
What am I doing wrong here?
Change
<ul>
<li v-for="validation in validation">{{ error }}</li>
</ul>
To:
<ul>
<li v-for="(error,index) in errors" v-bind:key="index">{{ error }}</li>
</ul>
In vue, you must provide a key for every v-for looping.
And change your data to:
data() {
return {
form: {
email: "",
password: ""
},
validation: [],
errors: [],
message: '',
canLogin: true,
};
},
I made your errors variable an arrayList.

Express.js with EJS application bug: the view throws a "Cannot read property 'toString'" error

I am working on a blogging application (click the link to see the GitHub repo) with Express, EJS and MongoDB.
I have Posts and Post Categories, each in its own collection.
The Categories Schema:
const mongoose = require('mongoose');
const categorySchema = new mongoose.Schema({
cat_name: {
type: String,
required: true
},
updated_at: {
type: Date,
default: Date.now()
},
created_at: {
type: Date,
default: Date.now()
}
});
module.exports = mongoose.model('Category', categorySchema);
The Posts schema:
const mongoose = require('mongoose');
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
short_description: {
type: String,
required: true
},
full_text: {
type: String,
required: true
},
category: {
type: mongoose.Schema.Types.ObjectId,
ref: 'category'
},
post_image: {
type: String,
required: false
},
updated_at: {
type: Date,
default: Date.now()
},
created_at: {
type: Date,
default: Date.now()
}
});
module.exports = mongoose.model('Post', postSchema);
There is some detail I have missed, because on updating a post, the editpost.ejs view returns a Cannot read property 'toString' of undefined error:
<form action="/dashboard/post/update/<%= post._id %>" method="POST" enctype="multipart/form-data" class="mb-0">
<div class="form-group">
<input type="text" class="form-control" name="title" value="<%= typeof form!='undefined' ? form.titleholder : post.title %>" placeholder="Title" />
</div>
<div class="form-group">
<input type="text" class="form-control" name="excerpt" value="<%= typeof form!='undefined' ? form.excerptholder : post.short_description %>" placeholder="Excerpt" />
</div>
<div class="form-group">
<textarea rows="5" class="form-control" name="body" placeholder="Full text">
<%= typeof form!='undefined' ? form.bodyholder : post.full_text %>
</textarea>
</div>
<% if (categories) { %>
<div class="form-group">
<label for="category">Choose a post category</label>
<select id="category" name="category" class="form-control">
<% categories.forEach(function(category, index) { %>
<option value="<%= category._id %>" <%=category._id.toString()==p ost.category._id.toString() ? 'selected' : ''; %>>
<%= category.cat_name %>
</option>
<% }); %>
</select>
</div>
<% } %>
<label for="postimage">Upload an image</label>
<div class="form-group">
<input type="file" name="postimage" id="postimage" size="20">
</div>
<div class="form-group d-flex mb-0">
<div class="w-50 pr-1">
<input type="submit" value="Update Post" class="btn btn-block btn-md btn-success">
</div>
<div class="w-50 pl-1">
Cancel
</div>
</div>
</form>
In the controller, the updatePost method looks like this:
exports.updatePost = (req, res, next) => {
const query = {
_id: req.params.id
}
const form = {
titleholder: req.body.title,
excerptholder: req.body.excerpt,
bodyholder: req.body.body
};
const errors = validationResult(req);
const post = {};
post._id = req.params.id;
post.title = req.body.title;
post.short_description = req.body.excerpt
post.full_text = req.body.body;
post.category = req.body.category;
if (req.file) {
post.post_image = req.file.filename;
}
if (!errors.isEmpty()) {
req.flash('danger', errors.array());
const categories = Category.find({}, (err, categories) => {
res.render('admin/editpost', {
layout: 'admin/layout',
website_name: 'MEAN Blog',
page_heading: 'Dashboard',
page_subheading: 'Edit Post',
categories: categories,
form: form,
post: post
});
});
} else {
Post.update(query, post, function(err) {
if (err) {
console.log(err);
return;
} else {
req.flash('success', "The post was successfully updated");
req.session.save(() => res.redirect('/dashboard'));
}
});
}
}
What have I missed?
I think your problem here is here:
post.category._id.toString()
When you pass category in req.body you are passing category._id as that parameter, so when you create that post object in your controller, you set post.category as req.body.category, when you return this to the view post.category is not an object with ._id property and the toString() method doesn't exist on it.
Try using post.category instead
Do nothing but handle req.file first in the post route, because you are using enctype="multipart/form-data".
Use req.file somewhere and it'll work fine! I don't know the exact reason but it works!

How to save data when you tried to reference the schema

I am trying to save information from a book that contains in its eschema auhtor and genre that I have it referenced in different files.
The problem is that when I make the reference in the main eschema. The book to have the reference of the author and the genre with the created book only keeps me the information of the book but does not make reference neither to the gender nor to the author
book.js
const mongoose = require('mongoose');
const bookSchema = new mongoose.Schema({
name: {
type: String,
},
author: {
type: mongoose.Schema.ObjectId,
ref: 'Author',
},
numberInStock: {
type: Number,
default: 0,
},
image: {
type: String,
default: '/path/to/default/image.png',
},
genre: {
type: mongoose.Schema.ObjectId,
ref: 'Genre',
},
});
module.exports = mongoose.model('Books', bookSchema);
author.js
const mongoose = require('mongoose');
const authorSchema = new mongoose.Schema({
name: String,
publicatons: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Book',
}],
});
module.exports = mongoose.model('Author', authorSchema);
createBook.ejs
<div class="container h-100">
<div class="row">
<div class="col-lg-6 col-md-offset-3">
<h1>Add a New Book</h1>
<form action="/books/new/create" method="post">
<div class="panel panel-default">
<div class="panel-body">
<div class="form-group">
<label for="book_name"> Book Name: </label>
<input type="text" class="form-control" name="name">
</div>
<div class="form-group">
<label for="exampleFormControlSelect1">Author</label>
<select class="form-control" name="<%= author._id %>">
<% author.forEach(function(authors){ %>
<option><%= authors._id %></option>
<% }); %>
</select>
</div>
<div class="form-group">
<label for="exampleFormControlSelect1">Genre</label>
<select class="form-control" name="<%= genre._id %>">
<% genre.forEach(function(genres){ %>
<option><%= genres.name %></option>
<% }); %>
</select>
</div>
<div class="form-group">
<label for="numberInStock">Number in stock: </label>
<input type="number" class="form-control" name="numberInStock">
</div>
<div class="form-group">
<label for="image">Image: </label>
<input type="text" class="form-control" name="image">
</div>
<button type="submit" class="btn btn-success">Success</button>
</div>
</div>
</form>
</div>
</div>
</div>
this is the part of the front where I try to send the data to the backent, but only manage to save, the name, image, quantity but not the referenced data.
controller.js
const create = (req, res, next) => {
if (!req.body) {
return res.status(400).json({
error: 'No data',
});
}
const book = new Books(req.body);
book.save((err, result) => {
if (err) {
return res.status(400).json({
error: err.message,
});
}
return res.status(201).json({
message: 'Book created succesfully',
result,
});
});
};
Routes controller
const express = require('express');
const router = express.Router();
const bookController = require('../controllers/book');
const booksave = require('../controllers/create');
const authorController = require('../controllers/author');
const genreController = require('../controllers/genre');
router.get('/books/home/:page', bookController.list);
router.get('/books/new', bookController.createTemplate);
router.post('/books/new/create', booksave.create);
router.get('/books/details/:id', bookController.bookDetail);
router.get('/books/new/create/genre', genreController.createGenreTemplate);
router.post('/books/new/genre', genreController.createGenre);
router.get('/books/new/create/author', authorController.createAuthorTemplate );
router.post('/books/new/author', authorController.createAuthor);
module.exports = router;
create new book render
controller.createTemplate = (req, res) => {
Author.find({}, (err, allAuthors) => {
if (err) {
console.log(err);
} else {
Genre.find({}, (err, allGenres) => {
if (err) {
console.log(err)
} else {
res.render('new', { author: allAuthors, genre: allGenres });
}
})
}
});
};
technically what I hope to obtain is that when I save the information of the book this has a reference to its author and to the genre and automatically the author has a references to the books that are referenced with the same
Of the values of the front I'm just getting
{name: 'Digital Fortress', numberInStock: '', image: ''}
const create = (req, res, next) => {
if (!req.body) {
return res.status(400).json({
error: 'No data received',
});
}
const book = new Books(req.body);
book.save((err, result) => {
if (err) {
return res.status(400).json({
error: err.message,
});
}
console.log(req.body);
return res.status(201).json({
message: 'Book created succesfully',
result,
});
});
};
Change the name attribute in <select class="form-control" name="<%= genre._id %>"> to name='author' same as in your bookSchema, and in <select class="form-control" name="<%= genre._id %>"> to name='genre'.
The problem is your sending a post request with the following body :
{ name: /*value*/ , <%= author._id%>: /*value*/, <%= genre._id%>: /*value*/, numberInStock: /*value*/, image: /*image*/ }
The constructor of your Book model recognizes name, numberInStock and image, but not <%=author_id%> and <%=genre._id%>.
Change this also:
author: {
type: mongoose.Schema.ObjectId,
ref: 'Author'
},
genre: {
type: mongoose.Schema.ObjectId,
ref: 'Genre'
}
to
author: {
type: mongoose.Schema.Types.ObjectId,
ref: ref: 'Author'
}
genre: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Genre'
}
You also need to pass genre._id instead of genres.name, and author._idas value attributes like this :
<form action="/books/new/create" method="post">
<select name="author">
<% author.forEach(function(authors){ %>
<option value="<%= authors._id %>" ><%= authors.name %></option>
<% }); %>
</select>
<select name="genre">
<% genre.forEach(function(genres){ %>
<option value="<%= genres._id %>" ><%= genres.name %></option>
<% }); %>
</select>
<button type="submit">Success</button>
</form>

Find MongoDB document from id posted as hidden input

How can I find the document matching the ID being posted in a hidden input?
Here is my schema:
var MessageSchema = Schema({
name: {type: String, required: true},
message: {type: String, required: true},
replies: [{ type: Schema.Types.ObjectId, ref: 'Comment' }]
}, {timestamps: true});
Here is my form:
<% for(var i=0; i<messages.length; i++) { %>
<form action="/comment/create" method="post">
<label>Name: </label>
<input type="name" name="name">
<label>Comment: </label>
<input type="text" name="comment">
<input type="hidden" name="replyTo" value=<%= messages[i]['_id']%> >
<button>Reply</button>
</form>
<% } %>
And my post route is a mess so at this point I just want to know how to find the dang message.
app.post('/comment/create', function(req, res) {
console.log(req.body.replyTo);
var message = Message.find({ _id: req.body.replyTo });
console.log(message);
res.redirect('/');
})
console.log(req.body.replyTo) returns the id 59022ff22951ce73ed9bb773.
console.log(message) returns undefined.
The call to Messages.find is asynchronous, and therefore will not provide a proper value when assigning it's result to the variable message. You need to handle this either with a callback or a Promise:
As a callback
Message.find({ _id: req.body.replyTo }, (err, res) => {
console.log(res); // message
});
As a Promise
Message.find({ _id: req.body.replyTo })
.then((res) => {
console.log(res); // message
})
.catch((err) => {
// ...
});

SequelizeJS Passing Array of Values

I am trying to figure out how it is possible to pass an array as the value for the property of an instance. I currently have the dataType set to STRING in my model and have values from jQuery fields insert each form field value into an array that I parse from the body and set to the property, discoverSource. Unfortunately I receive a string violation error that says I can't use an array or object. What does this mean and how can I change the dataType of the field or route to allow me to pass the comma separated values to the field?
E.x. For discoverySource I pass values to two fields (NJ, NY). On submit, the values are combined in an array as ["NJ", "NY"] and the error displays:
Error Message:
{"name":"SequelizeValidationError","message":"string violation: discoverySource cannot be an array or an object","errors":[{"message":"discoverySource cannot be an array or an object","type":"string violation","path":"discoverySource","value":["NJ","NY"]}]}
Here is my model:
module.exports = function(sequelize, DataTypes) {
var Organization = sequelize.define('organization', {
organizationId: {
type: DataTypes.INTEGER,
field: 'organization_id',
autoIncrement: true,
primaryKey: true
},
organizationName: {
type: DataTypes.STRING,
field: 'organization_name'
},
admin: DataTypes.STRING,
discoverySource: {
type: DataTypes.TEXT,
field: 'discovery_source'
},
members: DataTypes.STRING
},{
freezeTableName: true,
classMethods: {
associate: function(db) {
Organization.belongsToMany(db.User, { through: 'member', foreignKey: 'user_id' });
},
},
});
return Organization;
}
Here is the route:
var express = require('express');
var appRoutes = express.Router();
var passport = require('passport');
var localStrategy = require('passport-local').Strategy;
var models = require('../models/db-index');
appRoutes.route('/sign-up/organization')
.get(function(req, res){
models.User.find({
where: {
user_id: req.user.email
}, attributes: [ 'user_id', 'email'
]
}).then(function(user){
res.render('pages/app/sign-up-organization.hbs',{
user: req.user
});
})
})
.post(function(req, res, user){
models.Organization.create({
organizationName: req.body.organizationName,
admin: req.body.admin,
discoverySource: req.body.discoverySource
}).then(function(organization, user){
res.redirect('/app');
}).catch(function(error){
res.send(error);
console.log('Error at Post' + error);
})
});
Here is my view file:
<!DOCTYPE html>
<head>
{{> head}}
</head>
<body>
{{> navigation}}
<div class="container">
<div class="col-md-6 col-md-offset-3">
<form action="/app/sign-up/organization" method="post">
<p>{{user.email}}</p>
<input type="hidden" name="admin" value="{{user.email}}">
<input type="hidden" name="organizationId">
<label for="sign-up-organization">Company/Organization Name</label>
<input type="text" class="form-control" id="sign-up-organization" name="organizationName" value="" placeholder="Company/Organization">
Add Another Discovery Source
<div id="sign-up-organization-discovery-source">
<input type="text" id="discovery-source-field" placeholder="Discovery Source" name="discoverySource[0]">
</div>
<br />
<button type="submit">Submit</button>
</form>
Already have an account? Login here!
</div>
</div>
<script type="text/javascript">
$(function() {
var dataSourceField = $('#sign-up-organization-discovery-source');
var i = $('#sign-up-organization-discovery-source p').size();
var sourceCounter = 1;
$('#sign-up-add-discovery-source').on('click', function() {
$('<p><label for="discovery-source-field"><input type="text" id="discovery-source-field" size="20" name="discoverySource['+ sourceCounter++ +']" value="" placeholder="Discovery Source" /></label> Remove</p>').appendTo(dataSourceField);
i++;
return false;
});
$('#sign-up-organization-discovery-source').on('click', '.remove', function() {
if (i > 1) {
$(this).parent('p').remove();
i--;
}
return false;
});
});
</script>
</body>
To answer the last comment, I need to be able to make the code more readable, so I'm posting it here in a new answer.
Having thought about it a little more, it would make more sense to add it as custom 'getter' function. I'll also include the 'instanceMethods' to demonstrate how that works, as well.
var Organization = sequelize.define('organization', {
...
},{
freezeTableName: true,
classMethods: {
associate: function(db) {
Organization.belongsToMany(db.User, { through: 'member', foreignKey: 'user_id' });
},
},
// Here's where custom getters would go
getterMethods: {
discoverySources: function() {
return this.getDataValue('discoverySource');
}
},
// here's the instance methods
instanceMethods: {
getSourcesArray: function() {
return this.getDataValue('discoverySource');
}
}
});
Both of these options add the functions to each instance created by the Model. The main difference being in how they are accessed.
organization.discoverySources; // -> ['s1', 's2', etc...]
organization.getSourcesArray(); // -> ['s1', 's2', etc...]
note the additional () required on the instanceMethod. Those are added as functions of the instance, the getterMethods get added as properties.
setterMethods work the same way to allow you to define custom setters.
Hope that clarifies things a bit.

Categories