send data to a node view without losing form data - node.js

I have the following view
router.post('/save',
[
body('name').not().isEmpty().withMessage('name empty'),
body('surname').not().isEmpty().withMessage('surname empty'),
body('email').not().isEmpty().withMessage('email empty'),
body('password').not().isEmpty().withMessage('password empty')
],
(req, res)=>{
let errors = validationResult(req);
if (!errors.isEmpty()) {
req.session.msgs = errors.array({ onlyFirstError: true });
return res.redirect('/signup');
}
//save form data......
});
Ok, so I get some data from a login form and I want to go back to that login form , with some error messages.
It all works fine except that the form is empty after the redirection. If I put everything except, say password, I go back to the form and is empty , so I have to fill it again from scratch along. I want to go back and the name, surname and email be still there and re-enter just the password.
Is there a way to go back and whatever fields where not empty, still have their values?
Thanks
[node 8.11.1 + express 4.16.3 + express-validator]

As you are using node 8.x you can use one for core modules of nodejs which is
querystring. And using this, you can send the data in query string. The idea is to send the received parameters back to redirected view like
const params= querystring.stringify({
"name": body('name'),
"surname": body('surname'),
"email": body('email')
...
});
res.redirect('/signup?' + params);
Now these parameters will be available on your view and you can access them in singup view and fill the related fields

Related

Error: Can't set headers after they are sent - Ajax, Node, Express

I'm trying to using Airtable, node.js, express.js and jquery to create a simple user authentication functionality but I'm fairly new at this and I'm running into a problem I can't seem to fix and the articles I've read I can't seem to grasp or adapt to my particular situation.
I have this Ajax call in my html doc:
$("#checkUser").submit(function(e) {
var studentID = $('input[name="student"]').val()
e.preventDefault(); // avoid to execute the actual submit of the form.
var form = $(this);
var url = form.attr('action');
$.ajax({
type: "POST",
url: url,
data: form.serialize(), // serializes the form's elements.
success: function(data) {
$(window).attr("location", window.location.href + 'Dashboard?student=' + studentID);
},
error: function(data){
console.log("User not found. Try again");
}
});
});
This call sends the inputted username and data to the server which then processes it in the following way:
app.post('/checkUser', urlencodedParser, function(request,response){
var user = JSON.stringify(request.body);
user = JSON.parse(user);
base('RegisteredStudents').select({
filterByFormula: '{UserID} = ' + user.student,
view: "Grid view"
}).eachPage(function page(records, fetchNextPage) {
records.forEach(function(record) {
response.sendStatus(200);
});
fetchNextPage();
}, function done(error) {
response.sendStatus(404);
});
});
If the user exists in the database of Airtable, it should send '200' which the Ajax then reacts by redirecting accordingly to the user's profile. Otherwise, if the user does not exist, the server should respond with '404', which the Ajax call should react to by printing a statement in the console. While it does do these two things well, the server breaks down when, after a student puts in the wrong user ID and the Ajax prints the statement, the student tries to put once more a userID. I get the " Can't set headers after they are sent. " message. Please, how can I solve this?
Thank you!
You have two response.send..., you can only send data once. Either make sure only one runs with some conditional or add return before all response.send... so if any of them runs, the program will return and the other response.send.. will not run.

Node/Express/Jade passing username back to view

This is my first project with all these technologies, I typically do Angular, but I am working for a charity ( and keen to learn node ). This is day three.
During a login, this is the method called:
schema.statics.authenticateAndLoad = function(req, res, next) {
var userId = req.body.email;
res.locals.userId = userId;
This is supposed to store the value so it doesn't need to be reentered. If login fails, it does this:
return next('Login failed. Please enter your details and try again.');
Then in my Jade template:
if(locals.userId)
p Welcome to #{userId}
else
p Welcome
The actual template has code to try to do what I want:
input(type='text', name='email', id="inputEmail", placeholder="Email", value="#{body.email || ''}")
But this does not work.
So, I THINK this means my setting a value in the result is lost when I call 'next', but, because it's also passing in this error object to show on the screen, I'm not sure how I would go about making sure this error shows AND make the value pass through . Right now, my text just says 'welcome' and never 'welcome to xx', this is test code, I'm just proving the value is not being passed through.
My question is, what's the correct way to pass the value through to my template, and ALSO trigger the error message that is being shown.
The code, when I step in to it, goes deep in to Express and checks for errors there.
Hi it is good to see some developers doing charity work.
Typically what you would use is something like this assuming this is a piece of middleware
schema.statics.authenticateAndLoad = function(req, res, next) {
var userId = req.body.email;
res.locals.userId = userId;
loginFunction(userdetails,function(error,result){
//if there is an error you need to render the page or do a redirect to the
//login page
if(error){
var msg = 'Login failed. Please enter your details and try again.'
return res.render('login',{userId: userId,error: msg})
//this renders the login page and passes req.body.email to the page as userID and the msg as error
}
if(result){
//if this is correct you return next() as you are saying
//you may go through
return next()
}
}
}
then on your jade to display the error you use and userID
p #{userID}
p #{error}
So to answer your question the correct way to send the value to jade you pass it in using this and pass the error in as a variable.
return res.render(template_name,{userId: req.body.email,error: msg})
I hope this helps you out

Passing data from route handler to jade template

I'm putting together a basic project admin/management site and decided to finally learn to use node/express/monk/jade/redis, the works. Everything was going fine but I've run into a problem trying to get data passed between the route handler in index.js and the jade template file.
in index.js
exports.auth = function( db )
{
return function( req, res )
{
var userName = req.body.username,
userPassword = req.body.password,
authenticated = false;
// check credentials code
// ...
if (authenticated)
{
// set some session stuff
res.redirect( "home" ); // good to go
}
else
{
res.locals.err = "Authentication error";
res.redirect( "login" ); // show err on login page
}
}
}
in login.jade
- if (typeof( locals.err ) !== 'undefined' ) {
p.errormsg #{ locals.err }
- }
Iterating over locals in the jade template it doesn't show an entry for err. Does res.redirect() wipe out the err entry I made in index.js? Or am I actually dealing with two different objects (res.locals in index.js and locals in the jade template)?
My original approach was to use res.render( "login", { "err" : "Authentication err" } ) instead of redirecting, but I cannot figure out how to get the browser to show /login and not /auth when the error happens. I tried
res.location( "login" );
res.render( "login", { "err" : "Authentication err" });
but the browser still shows /auth.
The only other approach I found was using session data. The session object is available in both places and I can set/read the information from it as needed. The solution is inelegant though since the session info persists through reloads of the login page so the browser just keeps showing the error message for the original attempt rather than reloading/rendering a clean login page.
Any help is appreciated, and thanks in advance!
Yes - the redirect is returning a redirect to the client, which makes a separate request from the client. Your prior res.locals.err is long gone. You may want to read the doc on res.redirect().
Session data would be a sensible way to handle this unless you are a hardcore about statelessness. I am not sure why you find it inelegant. Why don't you reset that element of the session data after you render the next page?
There are different ways you can handle your issue about what the location bar shows if you search around for some javascript. Feels like a bit of kludge though.
Personally, I just have a /login path - called via GET it displays the login page, called via POST it authenticates, redirects if successful, or renders the login template with the error if the login is bad. No session data necessary.

JadeJS and pre written form values at re-rendering views

I'm using Node/Express and Jade to build and App.
I have a POST route that sanitize and validate form input and then save this to a MongoDB.
If this form input is'nt validated, the route will throw and error and the error handler will re-render this same page...
And here come the problem. In this re-render and want the form values be pre written and ready to be corrected by the user... I don't want a clean form where the user has to re-write everything.
I have tried to submit the req.body (sanitized) data to the re-rendered page, which works.
But if try to use this data in my Jade view, Node will output and error when this req.body data is not defined... Like when you enter this page for the first time, and have'nt entered any wrong inputs yet.
How do i solve this in a good way?
edit - Without a code sample I'm not sure if my example is more or less than you need.
If you render the form template immediately within the the form's POST request handler, you probably don't need to involve req.session. Just save the appropriate locals and render your template.
If you must redirect or otherwise need to have the values available across multiple requests, you can save them in req.session as shown below.
Either way make sure your Jade template handles all cases; in my example I test if(locals.savedValues) to decide whether to write defaults or saved values into the form.
Finally if the error is not jade related please paste that error.
Use req.session to save the values. Set up a locals variable to represent the saved values or null before you render the form.
app.get('/form', function(req, res){
res.locals.savedValues = req.session.savedValues || null;
res.locals.savedErr = req.session.savedErr || null;
res.render('form');
});
app.post('/form', function(req, res){
var values = {
name: req.body.name,
email: req.body.email,
};
validateForm(values, function(err, processed){
if(err){
req.session.savedValues = processed;
req.session.savedErr = err;
// req.session.savedValues = values, if you dont want to propose changes
res.redirect('back');
} else {
delete req.session.savedValues;
delete req.session.savedErr;
res.redirect('/success');
};
});
});
In your jade template, handle both cases:
if(locals.savedErr)
span.error=locals.savedErr
form(action='form', method='post')
if(locals.savedValues)
input#name(type='text')=locals.savedValues.name
input#email(type='text')=locals.savedValues.email
else
input#name(type='text')
input#email(type='text')

How to apply post redirect and get pattern in node.js?

I am testing with a very simple application in node.js where I create and save an application. I show the post form with the newPost function and I receive the post with the data in the savePost method. In the latter one I do a validation (with iform module) and I want to go show again the same page as before but filling the form with the data sent by the user and also with the errors found.
I have a similar code like this one. In it I render the same jade page if I find any error. It works though I want to apply the pattern redirect and get there as I don't want to send again the post request when the user presses F5.
So, how is the usual way to make a post redirect and get from the post method passing them all the parameters I have received adding the errors? Is there any module which can help to do so?
var prepareObject = function(req, res){
var errors = {};
if('iform' in req){
errors = req.iform.errors;
}
return {title: 'Nuevo Post', body:req.body, errors: errors};
};
// mapped as /newPost (type GET)
exports.newPost = function(req, res){
//show form to create post
res.render('newPost', prepareObject(req, res));
}
// mapped as /savePost (type POST)
exports.savePost = function(req, res){
if(req.iform.errors) {
//there are errors: show form again to correct errors
res.render('newPost', prepareObject(req, res));
}else{
//no errors: show posts
res.redirect('/posts');
}
}
You can redirect to GET "/newPost" instead of rendering the "newPost" template.
To have autocomplete working, you may either add the data to the redirect query (faster) and render it, or add the data to the session (don't forget to delete it after rendering), but the later option requires a session store.

Resources