NodeJS Redirecting WIth Single-Page JQuery Mobile - node.js

I'm working on a NodeJS/ExpressJS application using JQuery Mobile (JQM). I'm having trouble redirecting from the backend. My app dynamically generates buttons
<button class=\"setAttndingbtn btn btn-primary\" value=\"" + curr.place_id + "\">Attending: " + numAttending.numAttnd + " people are going</button>
after a $.getJSON search is made. Using promises/setTimeout, the following handler is attached to all buttons:
$('.setAttndingbtn').click(function(){
var buttonsir = $(this); //Store this button reference
if($(this).html().slice(0,1) == 'A'){ //Check the button's state
$.getJSON('/api/attending/' + $(this).val(), function(data){ //Ajax
$(buttonsir).text("You are attending! " + data.numAttnd + " people are going"); //Change state
});
} else {
$.getJSON('/api/attending/rmv/' + $(this).val(), function(data){
$(buttonsir).text("Attending: " + data.numAttnd + " people are going");
});
}
});
The relevant routes are here:
function isLoggedIn(req, res, next){
if(req.isAuthenticated()){
return next();
} else {
res.redirect('/login');
}
}
app.route('/login')
.get(function(req, res){
res.sendFile(p + "/public/login.html");
});
app.route('/api/attending/:number')
.get(isLoggedIn, searchServerInstance.setAttending);
On the backend, when an "Attending" button is clicked, I want to check if the user is logged in and if not, redirect them to the login page. When I run the code as is, in Firefox's Firebug console, I see the GET request and if I expand it, in the "Response" section, the HTML code to my login page is displayed. But the page doesn't actually load the file.
My /login.html has <div data-role="page">, header, and content tags, as JQM recommends. I've tried including the entire login page in my /index.html, but attempting to use res.redirect("/public/index.html#page2") results in a "file not found".
Is it possible to use Node/Express to tell JQM which <div data-role="page" id="page1"> to load? Is there a way to force JQM to load /login.html from the server-side, the way rel="external" allows for? Or will I have to ditch JQM?

Reading through the JQM documentation, I've figured out some ways to do this. For anyone else who encounters it, here's what I believe I understand:
If you don't care about maintaining the integrity of the single-page, you can look into turning off JQM's defaults, like ajaxEnabled, which turns off JQM's hash listening and ajax, loading URLs normally. I don't know if this works with Express's res.redirect because I haven't tried it.
What I opted to do was to replace res.redirect('/login.html') with a custom JSON response res.send({loginState: "LOGIN"}). Then, on the client side, within my $.getJSON request in the $('.setAttndngbtn') listener, i put the following code:
.
if(data.loginstate !== "LOGIN"){
$(buttonsir).text("You are attending! " + data.numAttnd + " people are going");
} else {
$(":mobile-pagecontainer").pagecontainer("change", '#page2');
}
And it works just fine! Here is the JQM documentation on the pagecontainer widget that allows in-page redirection with the change method or external redirection with the load method.

Related

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client. Unable to redirect different page than what it is supposed to be

In my program I am trying to make the server redirect the user to a different page if the wrong details of some sort are put in. The main problem that I am having is that whenever I try to use res.redirect() with another web page as a parameter I get the following error back: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I am not sure if I am referencing the web page in terms of where its stored on my computer wrong but something else about http headers was mentioned.
Here is my code that might potentially help to show what is wrong:
app.post("/profile.html", (req, res)=>
{
if(req.body.login_name == undefined)
{
var sql = "SELECT * FROM connectfour.players WHERE username =" + "'" + req.body.signup_name + "';";
connection.query(sql, function(error, results)
{
if(error) throw error;
if(results.length > 0)
{
console.log(res);
res.redirect("/start");
var already_taken = "username " + req.body.signup_name + " is already taken";
res.render("start", {already_taken});
}
else
{
var signup_name = req.signup_name;
res.render("profile", {signup_name});
}
});
}
So the form on another page posts the request for the profile.html page. Then something is checked to detect if the username is logging in or signing up. An SQL query happens with the data sent over. If the user already exists then the user will get sent over to a page that will make them try to signup again. This contains the res.redirect problem:
profile.ejs is what the page requested is supposed to be. But I need to redirect it to start.ejs if the if statement mentioning if(results.length > 0) happens. I would try to post an image showing what my problem is but the problem is that my reputation is not high enough.
You either call res.redirect or res.render. Both sets status and returns it and some info to a client. If '/start' is processed by some Express route handler (that renders using res.render("start", {already_taken});) when just redirect to it and that's all.

submitting form data without leaving page. Express, Node.js, Pug

I read through a similiar post but the individual was using perl and something elss so it didn't help me. My question is how can i submit a form with node js/pug but remain on the same page.
In the pug form the method is set to POST and action set to /profile
in my nodejs i'm using
router.post("/profile", req, res, next){
return res.redirect("back")
}
The problem is that all this does is reload the page. I want to stay on the page and simple show a message saying "profile update".
Then don't return a redirect. Simply take the post data and process it, you can return the state of the operation, for example. Then however you should use an AJAX query instead of stock form, which will always redirect you to the action URL.
router.post("/profile", req, res, next){
return updateProfile(req.body); // true or false
}
And in jQuery, for example, you can perform an AJAX request
$.ajax({
type: "POST",
url: "/profile",
data: profile_form_data_object,
success: function(data) {
alert("Result of the profile update was: " + data);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Pug and nodejs/express - button sends request

I have a nodeJS server using Express. It uses a pug template. It displays a bunch of images. I want it to display a button on each image. When that button is pressed, the server deletes the image and the client refreshes.
here is what I have so far for the .pug:
// register form
block content
form(name="getomdb",method='post')
div.input
input(type="submit",name="delete", value="Delete " + link[0])
div.container
h3#title
p#plot
link[0] references the image I want to delete. As far as I can tell, what this is doing is sending a POST request to my server with the value "Delete " + link[0].
My server attempts to handle it:
app.post('/', function(req, res){
console.dir(req.body);
console.log('post got');
res.render('index.pug', { links: links})
});
Apparently, there is no body to the request (it prints undefined). How I access which specific link it is trying to delete?
You should be able to access your value through
req.body.delete
Just add the name of the value you are passing in. Keep in mind if you ever start passing in json or other formats, you'll need to check out the "BodyParser" module.
app.post('/', function(req, res){
console.dir(req.body.delete); //add the .delete
console.log('post got');
res.render('index.pug', { links: links})
});

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.

Sending text to the browser

I have managed to get file uploading work in Node.js with Express, and in the code i'm checking whether it's an image or not that the user is trying to upload.
If the file was successfully uploaded I want to show a message to the user, directly to the HTML page with the uploading form. The same should be if the file the user tried to upload wasn't an image, or something else happened during the upload.
The code below works (res.send...) but it opens up a new page containing only the message.
My question is: How can I change my code so that the message is sent directly to the HTML page instead? If it could be of any use, i'm using Jade.
Thanks in advance!
app.post('/file-upload', function(req, res, next) {
var fileType = req.files.thumbnail.type;
var divided = fileType.split("/");
var theType = divided[0];
if (theType === "image"){
var tmp_path = req.files.thumbnail.path;
var target_path = './public/images/' + req.files.thumbnail.name;
fs.rename(tmp_path, target_path, function(err) {
if (err) throw err;
fs.unlink(tmp_path, function() {
if (err) {
throw err;
res.send('Something happened while trying to upload, try again!');
}
res.send('File uploaded to: ' + target_path + ' - ' + req.files.thumbnail.size + ' bytes');
});
});
}
else {
res.send('No image!');
}
});
from what I understand you are trying to send a message to an already open browser window?
a few things you can do,
Ajax it, send the post, and process the return info.
Submit it as you are doing now, but set a flash message (look at http://github.com/visionmedia/express-messages) and either res.render the form page, or res.redirect to the form function
now.js or a similar solution. This would let clientside use serverside functions and serverside code to run clientside functions. So what you would do would be on submit, pass the post values to a serverside function, which will process it and trigger a clientside function (display a message)
For my money option #2 is probably the safest bet, as clients without javascript enabled will be able to use it. As for usability #1 or #3 would give a more streamlined appearance to the end user.
You can use WebSockets. I recommend using Socket.IO, it's very easy to work with. On the client-side you would have an event-handler which would use JavaScript to append the new information to that page.
You could then have the server for example say:
socket.emit('error', "Something happened while trying to upload, try again!");
and the client would use:
socket.on('error', function(data){
//alert?
alert(data);
});
http://socket.io/#how-to-use

Resources