express-validator retain input values - node.js

Im new no NodeJS, ExpressJS and Jade. I'm using express-validator for validating a form.
In route:
req.assert('firstName', 'Name is required').notEmpty();
req.assert('lastName', 'Name is required').notEmpty();
req.assert('email', 'A valid email is required').isEmail();
var errors = req.validationErrors();
....
if (errors){
res.render('users/new', {
errors: errors
});
}
....
In Jade, to display error:
- if (errors)
div.alert.alert-error
ul
- each error in errors
li= error.msg
Above code works. I want the input values to be retain in the form so that user's don't to type it again.
input#name(type="text", name="name", value="?")
How?
Thanks

You can simply put back values to rendered view and interpolate it into input tag.
if (errors) {
res.render('users/new', {
errors: errors,
firstName: firstName,
...
});
}
and in jade template put
input#name(type="text", name="firstName", value="#{firstName}")
or
input#name(type="text", name="firstName", value= firstName)
it depends on your personal style.
It is better to define values like person.first, person.last and person.email, because you can put only person into your rendered view.

Your users/new.js file
var body = req.body;
var form = {
name: body.name,
surname: body.surname,
...
};
req.checkBody('name', 'Name is required').notEmpty();
req.checkBody('surname', 'Surname is required').notEmpty();
var errors = req.validationErrors();
if (errors) {
//Pass both errors and form objects as local variables
res.render('users/new', { errors: errors, form: form });
} else {
....//write to DataBase
}
new.handlebars file
{{#if errors}}
{{#each errors}}
<div class="alert alert-danger">{{msg}}</div>
{{/each}}
{{/if}}
<form class="" method="post" action="/users/new">
<p>Name*</p>
<input type="text" value="{{form.name}}" name="name" />
<p>Surname *</p>
<input type="text" value="{{form.surname}}"name="surname" />
</form>

Related

Update data in mongodb with input valors

I have three input to obtain three different values. Im using express.js , node.js, mongodb and ejs templates.
<form action="/save-profile/<%= user.id %>/<%= user.name %>/<%= user.lastname %>/<%= user.description %>" method="POST">
<div class="input-group mb-3">
<span class="input-group-text" id="basic-addon1">Name</span><%= user.username %>
<input type="text" class="form-control" placeholder="'John'" aria-label="Username" name="username">
<span class="input-group-text">lastName</span><%= user.lastname %>
<input type="text" class="form-control" placeholder="" aria-label="Server" name="lastname">
</div>
<div class="input-group">
<span class="input-group-text">Description:</span>
<textarea class="form-control" aria-label="With textarea" placeholder="" name="description"><%= user.description %></textarea>
</div>
</p><br>
<button class="btn btn-primary mb-10 btn-lg">Save</button>
</div>
</div>
In js file:
router.post('/save-profile', async (req, res) => {
const profile_id = await User.findById({ _id: req.body.id })
const updatedName = await User.findOneAndUpdate({ username: req.body.username})
const updatedlastname = await User.findOneAndUpdate({ apellido: req.body.lastname })
const updatedDescription = await User.findOneAndUpdate({ description: req.body.description })
console.log(profile_id,updatedName,updatedApellido,updatedDescription)
res.redirect('/profile')})
I tried to do it with a get but it didn't work
Firstly, action attribute in the form tag accepts the path where you are handling the form data. You only need to pass the user.id, there's no need to pass the other fields for this use-case.
<form action="/save-profile/<%= user.id %>" method="POST">
...
</form>
Secondly, in your route handler, the database record can be updated using only a single findOneAndUpdate call. You don't need to call it multiple times for every field if you're only going to update a single record.
The path written in action attribute will appear as /save-profile/1, for instance, in your route handler. Value preceding /save-profile/ i.e. 1 can be accessed by modifying the path in route handler as /save-profile/:id and in the callback you can get it by req.params.id
Finally you have this.
router.post('/save-profile/:id', async (req, res) => {
const response = await User.findOneAndUpdate(
{ _id: req.params.id },
{
username: req.body.username,
apellido: req.body.lastname,
description: req.body.description
},
{ new: true }
)
// Do something with response
res.redirect('/profile')
})

How to display express errors in ejs

I am validating emails users enter using "emailCheck" and a piece of code I found on another question, this is the code in my app:
app.post("/blog", (req, res) => {
const name = req.body.name;
const email = req.body.email;
emailCheck(email).then(() => {
const newSubscriber = {name: name, email: email};
Subscriber.create(newSubscriber).then(() => {
res.redirect("/blog")
})
.catch((error) => {
res.json({serverErrorEmailExistence: "This email adress is already in use!"})
})
})
.catch(() => {
res.json({serverErrorEmailExistence: "This Email doesn't exist!"})
})
})
This works as it is, but the errors are shown on a new blank page. I would like to show the error under the form that I have. Form is in included as a partial in my app.
Here is the form html:
<section id="emailSub">
<div id="emailContainer">
<h1>Subscribe to my Newsletter</h1>
<p>You will get weekly emails when a post is published.</p>
<form action="blog" method="POST" id="emailForm" autocomplete="off">
<div class="field">
<input type="text" placeholder="Name: " name="name" required>
</div>
<div class="field">
<input type="email" placeholder="Email: " name="email" required>
</div>
<button type="submit">Subscribe!</button>
</form>
</div>
<div id="thankYouMsg">
<h1>Thank you for subscribing!</h1>
<p><i class="far fa-check-circle"></i></p>
</div>
<button id="exitForm"><i class="fas fa-times"></i></button>
</section>
I include this on the blog main page with:
<%-include("partials/subscribe") %>
And here is my subscriber model:
const mongoose = require("mongoose");
const SubscriberSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
}
});
module.exports = mongoose.model("Subscriber", SubscriberSchema)
How can I show that error in the form?
The div with the ID thankYouMSg is shown after a successful form submit, usually it is hidden with Css.
I tried searching for this and I found a lot of answers but I either don't know how to include them in my code or I don't understand enough to search for the right answer (might be both). To be honest, I just included the emailcheck code in my app the best I know how. I don't really understand what .catch(error) is delivering.
Thank you
Following the answer I tried:
.catch(() => {
res.render("/blog", {errorMessage: "This email adress is already in use!"});
})
})
.catch(() => {
res.render("/blog", {errorMessage: "This Email doesn't exist!"})
})
But, I get the "cannot look up view /blog in views". I tried the same with
res.redirect and it just loads without anything happening.
What's happening is that in case of an error, you catch this error and return a json-response which the browser cannot render directly in html.
What you can do instead, is re-send your subscribe page and pass the caught error message to that page, which you can render there. Something like this should help you get started:
in your app.js
...
.catch(() => {
res.render("your-subscribe-template.ejs", {
errorMessage: 'This Email doesn\'t exist!'
});
});
...
in your template.ejs:
...
<% if (typeof errorMessage !== "undefined") { %>
<p>Form could not be submitted due to the following error:</p>
<p><%= errorMessage %></p>
<% } %>
...

Redirect response into a dynamic url

I am using node js with:
express framework
hbs framework
mongodb
My task is to redirect my response to a url with a format /users/:name/course/:courseId.
After running the code with the parameters name=James and courseId=1234, I get /users/James/course/1234 in the browser url. However, I am not able to post to /users/James/course/1234.
Edit: the data is being successfully posted, but I am getting a message Cannot GET /users/James/course/1234.
I have the following code:
app.js:
app.get('/', (req, res) => {
res.render('form.hbs')
})
app.post('/saveUsers', [
// firstname must contain letters only
check('firstname', 'First name should contain only letters').isAlpha(),
// email must be in a email format
check('email', 'Email field must be in a email format').isEmail(),
// courseId must be numerical and exactly 4 digits
check('courseId', 'course ID should contain only numbers').isNumeric(),
check('courseId', 'course ID should be exactly 4 digits').isLength({ min: 4, max: 4 })
], (req, res) => {
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array(), data:req.body})
}
var db = utils.getDb()
db.collection('users').insertOne({
name: req.body.firstname,
email: req.body.email,
courseId: req.body.courseId
}, (err, result) => {
if (err) {
Response.send('Unable to insert a student')
}
console.log(result.ops)
nameUrl = result.ops[0].name
courseIdUrl = result.ops[0].courseId
res.redirect(`/users/${nameUrl}/course/${courseIdUrl}`)
})
})
app.post(`/users/${nameUrl}/course/${courseIdUrl}`, (req, res) => {
res.json(result.ops)
})
form.hbs:
<!DOCTYPE html>
<html>
<body>
<h1>Welcome</h1>
<p>Enter your name, email and course ID:</p>
<form action="/saveUsers" method="POST" id="myForm">
<input type="text" placeholder="firstname" name="firstname">
<input type="email" placeholder="email" name="email">
<input type="text" placeholder="courseId" name="courseId">
<input type="submit" value="Submit">
</form>
</body>
</html>
It looks like you are using template literal string interpolation where you should be using the Express syntax for route parameters. See the "route parameters" section in the docs.
Instead of:
app.post(`/users/${nameUrl}/course/${courseIdUrl}`, (req, res) => {
res.json(result.ops)
})
Try:
app.post(`/users/:name/course/:courseId`, (req, res) => {
res.json(result.ops)
})

Editing text on a page in node js

I am trying to implement a simple edit feature in my app. In my profile.handlebars file, I have an edit button. When clicked, I'd like the user's information to appear in the text input fields on the form in order to allow the user to edit their existing information.
Right now, they would have to input all of their information over again (and every field in the form would need to be filled out due to validation that I have implemented), click Submit, and their profile can be updated. Is this possible without using a framework (like Angular)? For example, in LinkedIn, a user can hover over a section of their profile causing the edit buttons to highlight, then click a single edit button, and they're instantly in editing mode. That might be too advanced for my purposes right now, but eventually, I'd love to have functionality like that.
I have a post request in my routes file to handle a user posting information to their profile:
router.post('/add', function(req, res) {
req.checkBody({
'city': {
errorMessage: 'Please enter your city'
},
'state': {
errorMessage: 'Please enter your state',
notEmpty: true
},
'zip': {
errorMessage: 'Please enter your zip code',
notEmpty: true
},
'about': {
errorMessage: 'Please briefly describe yourself',
notEmpty: true
}
});
console.log("req.user " + req.user);
var errors = req.validationErrors();
if (errors) {
res.render('profile', {
errors: errors
});
} else {
var user_info = new User_Info({
city: req.body.city,
state: req.body.state,
zip: req.body.zip,
about: req.body.about,
user_name: req.user.username
});
user_info.save(function(err, user_info) {
if (err) throw err;
});
res.redirect('profile/' + req.user.username)
}
})
Then, I have my profile.handlebars file:
{{#if errors}}
Uh oh! Something went wrong. Please review the below errors, and try again.<br><br>
<ul>
{{# each errors }}
<li style="color: red">{{this.msg}}</li>
{{/each}}
</ul>
{{else}}
<h3 align="center">Profile ({{user_name.name}})</h3>
<div class="row">
<div class="col-md-4 col-md-offset-4">
<div class="thumbnail" style="border-radius: 12px">
<div class="caption">
<p>City: {{# each information }} {{this.city}} {{/each}}</p>
<p>State: {{# each information }} {{this.state}} {{/each}}</p>
<p>Zip: {{# each information }} {{this.zip}} {{/each}}</p>
<p>About: {{# each information }} {{this.about}} {{/each}}</p>
<div class="btn-group">
<button type="Edit" class="btn btn-danger dropdown-toggle deleteLocation" data-id="{{this.id}}">Edit</button>
</div>
</div>
</div>
</div>
</div>
<br>
<center>
<form method="POST" action="/users/add">
<input type="text" name="city" placeholder="City" style="text-align: left">
<br><br>
<input type="text" name="state" placeholder="State" style="text-align: left">
<br><br>
<input type="text" name="zip" placeholder="Zip" style="text-align: left">
<br><br>
<textarea name="about" placeholder="About You" style="text-align: left; resize: both;" rows="5" cols="50"></textarea>
<br><br>
<div class="btn-group">
<button type="submit" class="btn btn-success dropdown-toggle" aria-haspopup="true" aria-expanded="false">Submit</button>
</div>
<br><br>
</form>
</center>
{{/if}}
Please let me know if you need additional info to help me solve this issue. Thanks!
you can use this code for node for editing the parameters , city,state,zip and about.
router.post('/add', function (req, res) {
var users = req.Collection;
var city = req.body.city;
var state = req.body.state;
var zip = req.body.zip;
var about = req.body.about;
var user_id = req.body.user_id;
if (city && state && ) {
users.findOneAndUpdate({_id: user_id}, {$set: {city: city, state: state, zip: zip, about:about}}, function (err, user) {
if (err) {
res.json({status: 0, message: err});
}
if (!user) {
res.json({status: 0, msg: "not found"});
} else {
res.json({status: 1, city: city, state: state, zip: zip, about:about, message: " edit success"});
}
})
} else {
res.json({status: 0, msg: "Invalid Fields"});
}
});

Node.js (Express) Form Clears on Submission

I am working on a really basic registration form in Node.js (with Express), and I am trying to find the easiest way to provide basic form validation. I've gone with "Express-Validator", which seems to do a fine job. However, my goal is to simply show any validation messages that are required and to leave the values entered by the user alone.
It seems that the request information is not making it back into the res.render, which I guess makes sense. However, I've looked everywhere I can think of and I can't find any reference that discusses how to keep form fields populated after showing error messages.
Below is a small snippet describing my approach:
post: function(req, res){
var userName = req.body.username;
var password = req.body.password;
//Validate input
req.assert("username", 'Invalid email address.').isEmail();
req.assert("password", 'Password cannot be empty.').notEmpty();
req.assert("passwordConfirm", 'Passwords entered do not match!').equals(password);
//Make sure we have no validation errors
var pageErrors = req.validationErrors();
if(!pageErrors)
{
userModel.CreateUser(userName, password, function(err){
if(err)
{
//there was a problem inserting new user... probably already exists
//will need to check the error to confirm
var dbErrorMessage = "Could not insert record into database!";
if(err.code === 11000)
{
//this is a duplicate entry
dbErrorMessage = "A user with that email address already exists!";
}
res.render('register.html', { pageErrors: [{msg: dbErrorMessage }]});
}
else
{
res.render('register.html', { successMessage: successMessage });
}
});
}
else
{
res.render('register.html', { pageErrors: pageErrors });
}
Unfortunately, you have to repopulate the form manually. If you get any page errors, you will pass back the form values to the view.
if(!pageErrors)
{
// ...
}
else
{
res.render('register.html', {
pageErrors: pageErrors,
userName: userName
});
}
And in your view, you would do a simple check to see if their are any errors and repopulate accordingly. You would have to keep track of what errors are produced for each form field.
<% if (userNameError) { %>
<input type="text" name="userName" value="<%- userName %>" />
<% } else { %>
<input type="text" name="userName" />
<% } %>
Another popular way is to send your form via ajax to to the server, and do all your validations. If there is an error, the entered form data remains and you would show the error, otherwise redirect after the successful login. Below is an example of how to submit a form with javascript.
$("#login-button").live("submit", function (e) {
// this will prevent the form from being uploaded to the server the conventioanl way
e.preventDefault();
// the form data
var data = $(this).serialize();
// this logs the user in
$.ajax({
type: 'POST',
url: BASE_URL + '/login',
data: data,
dataType: 'json',
success: function (data, status) {
// successful
},
});
// superfluous fallback
return false;
});
There is an easy way is you are using
app.use(express.bodyParser()) and app.use(expressValidator());
You can use req.body
res.render('register.html', {
pageErrors: pageErrors,
validated: req.body
});
And I'm not sure which templating language you are using but you could do something like..
<input type="text" name="userName" value="<%= pageErrors.userName.value || validated.userName %>" />
This then gives back the good input if ok or the bad input if it needs correcting.
You can get this done using connect-flash
Below are the code snippets in different files in order to get the values entered by the user back in form when validations fail while doing signup with passport.
Run this below command to add new package into package.json
npm install connect-flash --save
app.js
var flash = require('connect-flash');
app.use(flash()); // add this above passport initialize
app.use(passport.initialize());
app.use(passport.session());
config/passport.js (Please focus on form data loading into flash)
passport.use('local.signup', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
}, function (req, email, password, done) {
req.checkBody('first_name', 'Firstname is missing').notEmpty();
req.checkBody('last_name', 'Lastname is missing').notEmpty();
req.checkBody('email', 'Invalid email').notEmpty().isEmail();
req.checkBody('password', 'Password is too short. Minimum size is 6.').notEmpty().isLength({min:6});
req.checkBody('confirm_password', 'Password and confirm password didn\'t not match').equals(req.body.password);
var errors = req.validationErrors();
if (errors) {
var messages = [];
errors.forEach(function(error) {
messages.push(error.msg);
});
req.flash('formdata', req.body); // load form data into flash
return done(null, false, req.flash('error', messages));
}
User.findOne({'email': email}, function (err, user) {
if (err) {
req.flash('formdata', req.body); // load form data into flash
return done(err);
}
if (user) {
req.flash('formdata', req.body); // load form data into flash
return done(null, false, {message: 'Email is already in use.'});
}
var newUser = new User();
newUser.first_name = req.body.first_name;
newUser.last_name = req.body.last_name;
newUser.email = email;
newUser.password = newUser.encryptPassword(password);
newUser.save(function(err, result) {
if (err) {
return done(err);
}
return done(null, newUser);
});
});
}));
routes/index.js (Please focus on form data in flash loaded back into a variable)
router.get('/signup', function (req, res, next) {
var messages = req.flash('error');
var formdata = req.flash('formdata'); // Get formdata back into a variable
res.render('user/signup', {csrfToken: req.csrfToken(),
messages: messages, // pass it here to access in view file
hasErrors: messages.length > 0,
formData: formdata[0]
});
});
router.post('/signup', passport.authenticate('local.signup', {
badRequestMessage: 'Please fill the form with all details',
failureRedirect: '/user/signup',
failureFlash: true
}), function (req, res, next) {
if (req.session.oldUrl) {
var oldUrl = req.session.oldUrl;
req.session.oldUrl = null;
res.redirect(oldUrl);
} else {
res.redirect('/user/profile');
}
});
views/signup.hbs (Please focus on values in input elements)
<form class="wow fadeInUp animated" data-wow-delay=".7s" action="/user/signup" method="post" >
<input type="text" placeholder="First Name" name="first_name" value="{{ formData.first_name }}">
<input type="text" placeholder="Last Name" name="last_name" value="{{ formData.last_name }}">
<input type="text" class="email" placeholder="Email Address" name="email" value="{{ formData.email }}">
<input type="password" name="password" value="" class="lock" placeholder="Password">
<input type="password" name="confirm_password" value="" class="lock" placeholder="Confirm Password">
<input type="hidden" name="_csrf" value="{{ csrfToken }}">
<input type="submit" name="Register" value="Register"></form>
Hope this helps.
set a variable for all the input, for example
var inputData = {
firstname : req.body.firstname,
lastname : req.body.lastname,
email : req.body.email,
username : req.body.username,
password : req.body.password,
password_confirmation : req.body.password_confirmation,
agreetoterms: req.body.agreetoterms
}
and then pass that variable to the view
res.render('register.html', { pageErrors: [{msg: dbErrorMessage }], inputData: inputData });
then in your view
value="<%= inputData.userName %>"
if you are using jade and form Validator from npm , the best part is that you can an if statement in jade and then you simply check if error then with res.render we send the objects also . See This
if(errors){
res.render('register',{
errors : errors,
name : name,
email : email,
username : username,
password : password,
password2 : password2
});
And in jade you do this
input.form-control(name='name',type='text',placeholder='Enter Name',value = (errors ? '#{name}':''))
so if there is errors value will set to variable in name which will rendered when we send back
I think you can also be done in Angular2/Angular.js
Well, there is a simple way to achieve this. Do following in controller:
else{
res.render('register.html', { pageErrors: pageErrors, inputData: req.body});
}
Now, in your ejs (or do it according to your view engine) file do following:
<input type="text" name="name" value="<% if(typeof pageErrors !== "undefined"){ %> <%- inputData.name %> <% } %>" class="form-control">
check http://www.quietless.com/kitchen/building-a-login-system-in-node-js-and-mongodb/
on register.html make this
var data = {};
data.user = $('#user-input').val();
data.email = $('#email-input').val();
data.pass = $('#pass-input').val();
$.ajax({ url: '/signup'
, type: 'POST'
, data: JSON.stringify(data)
, contentType: 'application/json'
, dataType: 'html'
})
.done(function(data) {
if (data == 'ok') {
$('#content').html('You are registered!');
}
else $('#account-form-container').append('<br>error:' + data);
});
there might be error like: CANNOT POST /
in this case the author of tutorial on link above uses lib $.ajaxForm
you can also use https://github.com/felixge/node-formidable
or $('#myform').submit() replace to $('#submit-a-link').click()

Resources