Jade html not updated after a redirect in Express.js - ajax

I'm currently having some trouble displaying a flash message in Express.js using Jade's templating engine and connect-flash. I am simply trying to flash an error message when the user tries to add a new User object to my database that already exists. However the flash message is not showing up on my page after calling router.post and redirecting back to the index (code below).
Through various console.logs and debugging, I have found that the data I am posting is indeed posting correctly, and the flash message is being set. What I have found is that on the redirect, all of the correct data is passing to the Jade template, but the variables are not being updated in the file itself. I am now wondering if this is a session related issue, or just something Flash/Jade/Express related that I am completely overlooking?
In the code below I am logging session data as well as setting the flash message to a variable. If the array for the flash message(s) is empty (i.e. on page load), an array is set with a message that says so. If the flash message(s) array contains a flash message, the test array is set with a message that says so.
index.js:
router.get('/', function(req, res, next) {
console.log(req.session);
var testArray;
var errorMessages = req.flash('user-error');
if (errorMessages.length === 0)
testArray = ['errorMessages is empty'];
else
testArray = ['errorMessages contains a message now'];
console.log(errorMessages);
console.log(testArray);
res.render('index', {
message: errorMessages,
tester: testArray,
...other irrelevant vars being passed...
});
});
router.post('/add', function(req, res, next) {
var ajaxData = req.body;
console.log(ajaxData);
User.findOne({name: ajaxData.name}, function(err, user) {
if (err) return console.error(err);
// if User DNE already in DB
if (user === null) {
...new user created and saved here...
}
/*where the important stuff begins*/
else {
console.log("flash message set");
req.flash('user-error', "A user with that name already exists!");
}
// redirect to index
res.redirect('/');
});
});
In my Jade template, I'm again logging errorMessages and testArray to make sure everything is passed to the file correctly (it is) then showing the variables.
index.jade
-console.log(message);
-console.log(tester);
.error-box Error: #{message}
.error-box Error: #{tester}
Initially loading the page, I will get the following HTML output:
<div class="error-box">Error: </div>
<div class="error-box">Error: errorMessages is empty</div>
No surprises here. But when I submit the form with data that sets the error flash message, I get the updated logs from router.get('/') and index.jade with both the correct errorMessages and testArray variables. However my HTML output remains the same:
<div class="error-box">Error: </div>
<div class="error-box">Error: errorMessages is empty</div>
Clearly the variables being passed to Jade are being updated correctly, but it appears that Jade is simply not updating the HTML. With my somewhat limited knowledge of how connect-flash and Jade work, this would lead me to believe that this is a session related issue, however my code in app.js appears to be setup correctly...
var session = require('express-session');
var flash = require('connect-flash');
app.use(session({
secret: 'secret',
cookie: { maxAge: 60000 },
resave: false,
saveUninitialized: false
}));
app.use(flash());
I am relatively new to Express.js, so I feel like there might be something small I am overlooking or don't understand, but I've tried to be as detailed as possible so I'm hoping someone can help me out here!

After more careful inspection, I found that what was really causing the issue was that res.redirect('/') was not running, as I was attempting to use AJAX on the client side to call router.post('/add').
I solved this by simply removing my AJAX request, then going back into my HTML and changing my form's attributes (the form whose data I was sending via AJAX) to include method="POST" and action="/add". This is the proper way to make a SERVER SIDE call to my router.post('/add').
I found that someone was having the same problem here, and this question initially led me to look into the AJAX/Client Side vs. Server Side issue. I found the latter question in a comment from #herbyme on this post.

Related

AJAX postdata working on localhost but not on (Apache) server

I identified an issue with postdata not being sent through AJAX on my server. To debug it, I wrote the following fairly minimalistic piece of javascript to test a simple AJAX call :
function my_custom_ajax(target_page, target_element , postdata_contents) {
// Sending the XMLHttpRequest as postdata
var xhr = new XMLHttpRequest();
xhr.open("POST", target_page, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;");
xhr.setRequestHeader("Content-length", postdata_contents.length);
xhr.setRequestHeader("Connection", "close");
xhr.send(postdata_contents);
// Waiting for the request to return
xhr.onreadystatechange = return_data;
// If all went well, we update the target element
function return_data()
{
if(xhr.readyState === 4)
{
if(xhr.status === 200)
{
// We update the target element
document.getElementById(target_element).innerHTML = xhr.responseText;
}
// Throw error in case of 404 or such
else
document.getElementById(target_element).innerHTML = "XHR can't be loaded";
}
// Throw error in case request got interrupted or didn't work out
else
document.getElementById(target_element).innerHTML = "XHR error";
}
}
It is called with the following HTML :
<div onClick="my_custom_ajax('test_page.php?xhr','my_id','postdata_test');">
Click me
</div>
<div id="my_id">
xhr response will appear here
</div>
And calls a PHP page which contains only this :
exit(var_dump($_POST));
When running this piece of code in my Apache localhost or another Apache server I own, it does pass whatever is in postdata_contents as postdata. The exit(var_dump($_POST)); does show that it works properly, and does print the value of the postdata I passed to it.
However, when running this same piece of code on the Apache server where it does not work, all I get is « array(0) { } », as in, no postdata is passed according to the PHP file.
Here is Firefox's dev tool view of the request details (in french, sorry, but should be obvious what is what) :
The debug tool shows that the postdata contents are properly being sent :
However, the returned content show that the postdata was somehow not passed :
On my localhost and my other Apache server, everything is exactly the same until the very last step, where the postdata is properly passed (the var_dump message is styled but you can easily see the gist of it : postdata_test is part of $_POST) :
After hours of fiddling with the configuration of this Apache server and trying all of the debug methods and breakpoints I could think up, my nerves are too worked up to continue thinking about this rationally for now. As I have no option of using another server or just copypasting my local Apache configuration file on the new server, I defer this question to you all, hoping that somebody can figure it out or once encountered something similar.
Thanks in advance,
Eric B.
Solved it myself by accident, I had mod_dumpio activated on the server and it started working once I turned it off.
I do not know what mod_dumpio was doing to deny XHR POST but not generic HTTP POST, but at least that's solved.
Hope this will help someone else some day.
(on a sidenote, I realize the postdata query in my example was malformed, should have been « postdata_test= » instead of « postdata_test », so add that equal sign if you are stuck in my situation and want to run the same tests I did)

Redirect from method in Vue.js with Vue-router older than version 1.x.x

I'm not much of a frontend developer but I know enough javascript to do the minimum.
I'm trying to plug into a last piece of login however my vue components are:
"vue-resource": "^0.9.3",
"vue-router": "^0.7.13"
I'm not experienced enough to move up to v1 or v2 respectively.
I would like to achieve something similar to this.
However I'm not getting a successful redirect.
my app.js file:
var router = new VueRouter();
...
import Auth from './services/auth.js';
router.beforeEach(transition => {
if(transition.to.auth &&!Auth.authenticated)
{
transition.redirect('/login');
}
else
{
transition.next();
}
});
```
In my login.js file
```
methods: {
/**
* Login the user
*/
login(e) {
e.preventDefault();
this.form.startProcessing();
var vm = this;
this.$http.post('/api/authenticate',
{ email : this.form.email,
password : this.form.password
})
.then(function(response){
vm.form.finishProcessing();
localStorage.setItem('token', response.data.token);
vm.$dispatch('authenticateUser');
},
function(response) {
if(response.status == 401)
{
let error = {'password': ['Email/Password do not match']};
vm.form.setErrors(error);
}else{
vm.form.setErrors(response.data);
}
});
}
}
I tried to do as suggested:
vm.form.finishProcessing();
localStorage.setItem('token', response.data.token);
vm.$dispatch('authenticateUser');
vm.$route.router.go('/dashboard');
However all it did was append the url on top.
I see that the 3 previous events were successfully done but not the redirect.
it went from:
http://dev.homestead.app:8000/login#!/
to
http://dev.homestead.app:8000/login#!/dashboard
when I need the entire page to go to:
http://dev.homestead.app:8000/login/dashboard#1/
I think i have a missing concept in order to do the redirect correctly.
UPDATE
As per suggested i have added param: append => false but nothing happens.
what i did afterward was within app.js create a method called redirectLogin() with console.log() outputs - that worked. what i did further is i put vm.$route.router.go inside there and called the method via vm.$dispatch('redirectLogin'); and that also didn't work.
NOTE:
The HTML is being rendered in Laravel first. the route I originally had (and working) as login/dashboard and that route is available via normal Laravel route. the blade is being rendered via view template.
So far I've been trying to vue redirect over to login/dashboard (not working) perhaps I should somehow remove login/dashboard and use the route.map and assign login/dashboard?
I would rather keep the login/dashboard as a laravel route due to authentication and other manipulation.
For Vue 2
this.$router.push('/path')
As par the documentation, router.go appends the path in the current route, however in your case it is appending along with # in the router as well.
You can use param: append, to directly arrive at your desired destination, like following:
vm.$route.router.go({name: '/login/dashboard#1/', params: {append: false}})
Edited
If it is not happening, you can try $window.location method like following:
var url = "http://" + $window.location.host + "login/dashboard";
console..log(url);
$window.location.href = url;
I think their is a misunderstanding here of how vue-router works. It seems you are not wanting to load a new route with a corresponding component, rather you simply want to redirect to a new page then let that page load and in turn fire up a fresh instance of vue.
If the above is correct you don't need vue-router at all. Simply add the below when you need to load the page:
window.location.href = '/login/dashboard'
If you'd rather simulate a redirect to that page (no back button history) then:
window.location.replace('/login/dashboard')
EDIT
The above would be fired when you have finished all processing that the page must run to set the users state which the next page requires. This way the next page can grab it and should be able to tell the correct state of the user (logged in).
Therefore you'll want to fire the redirect when the Promise has resolved:
.then(function(response){
vm.form.finishProcessing()
// store the Auth token
localStorage.setItem('token', response.data.token)
// not sure whether this is required as this page, and in turn this instance of vue, is about to be redirected
vm.$dispatch('authenticateUser')
// redirect the user to their dashboard where I assume you'd run this.$dispatch('authenticateUser') again to get their state
window.location.replace('/login/dashboard')

AJAX response returns current page

I was searching for a similar issue for a while now, but none of the solutions worked for me (and I couldn't find exactly the same issue).
First of all, the website I'm working on is running on Zend Framework. I suspect that it has something to do with the issue.
I want to make a pretty basic AJAX functionality, but for some reason my response always equals the html of the current page. I don't need any of Zend's functionality, the functions I need to implement could (and I'd prefer them to) work separately from the framework.
For testing purposes I made it as simple as I could and yet I fail to find the error. I have a page "test.php" which only has a link that triggers the ajax call. Here's how this call looks:
$('.quiz-link').click(function(e){
e.preventDefault();
$.ajax({
URL: "/quiz_api.php",
type: "POST",
cache: false,
data: {
'test': 'test'
},
success: function(resp){
console.log(resp);
},
error: function(resp){
console.log("Error: " + reps);
}
});
});
And this quiz_api.php is just:
<?php
echo "This is a test";
?>
When I click on the link I get the entire HTML of the current page. "This is a test" can't be found there. I'm also getting an error: "Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check http://xhr.spec.whatwg.org/."
I reckon it has to do with the JS files that are included into this HTML response, but I've also tried setting "async: true" and it didn't help.
I would like to avoid using Zend Framework functions for this task, because I'm not well familiar with it and even making a simple controller sounds rather painful. Instead I want to find out what's causing such behavior and see if it can be changed.
PS: I've also tried moving quiz_api.php to another domain, but it didn't change anything.
I know that it might be an older code but it works, simple and very adaptable. Here's what I came up with. Hope it works for you.
//Here is the html
Link Test
<div id="test_div"></div>
function test(){
// Create our XMLHttpRequest object
var hr = new XMLHttpRequest();
// This is the php file link
var url = "quiz_api.php";
// Attaches the variables to the url ie:var1=1&var2=2 etc...
var vars = '';
hr.open("POST", url, true);
//Set content type header information for sending url encoded variables in the request
hr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// Access the onreadystatechange event for the XMLHttpRequest object
hr.onreadystatechange =
function(){
if(hr.readyState == 4 && hr.status == 200){
var return_data = hr.responseText;
console.log(return_data);
document.getElementById('test_div').innerHTML = return_data;
}else{
document.getElementById('test_div').innerHTML = "XMLHttpRequest failed";
}
}
//Send the data to PHP now... and wait for response to update the login_error div
hr.send(vars); // Actually execute the request
}
you can change the whole page with a document.write instead of changing individual "div"s

Sammy intercepts a POST that is not one of the added routes

I have an application that uses Sammy for some simple client-side routing.
One of the pages has a "Download Pdf" button, which needs to do a POST to get and download a pdf document (not very resty, I know, but it has to be a POST due to the large amount of data I'm submitting). It does this using the old trick of dynamically creating, populating, and submitting a <form> element.
Everything works fine, except for I can see in the console an error from sammy that my route was not found. Note that this is not a route, or even a verb that Sammy should be handling.
Here is my reduced test case
Sammy(function initializeClientRouting(app) {
app.get('#/', show('#default'));
app.get('#/test', show('#test'));
function show(selector) { return function() {
$('section').slideUp();
$(selector).slideDown();
}; }
}).run('#/');
$('button').click(function() {
var form = $("<form method=post action: 'http://www.google.com'>").hide();
$('<textarea name=q>').text("search text").appendTo(form);
form.appendTo('body').submit().remove();
});
Does anyone know how to prevent this error? Is this a bug in Sammy?
It's a combination of sammy & JQuery behaviour (bug?). When generated dynamically the way you put it, the form tag is being rendered as
<form www.google.com'="" 'http:="" action:="" method="post">
This will try to POST to the current page which probably is something like
http://blah/# or http://blah/#/test
For some reason, Sammy will be triggered because of the hashtag, not finding a POST configured and log an error.
Fiddling with your example, what worked for me was:
var form = $("<form>");
form.attr('method', 'post');
form.attr('action', 'http://www.google.com');
$('<textarea name=q>').text("search text").appendTo(form);
form.appendTo('body').submit().remove();
This seemed to generate the proper HTML and remove the Sammy error.

Node.js, Express and Jade - Forms

I'm using Node.js, Express and Jade and I'm trying to figure out how to post, validate & process form data.
In my jade file I create a contact form:
div#contact-area
form(method='post',action='')
label(for='name') Name:
input(type='text',name='name',id='name')
label(for='email') Email:
input(type='text',name='email',id='email')
input(type='submit',name='submit',value='Submit').submit-button
I'm then utilising the module 'express-validator' to validate the form as follows:
var express = require('express')
,routes = require('./routes')
,http = require('http')
,path = require('path')
,expressValidator = require('express-validator')
;
var app = express.createServer();
app.configure(function(){
app.set('views', __dirname + '/views');
app.set('view engine', 'jade'); //not needed if we provide explicit file extension on template references e.g. res.render('index.jade');
app.use(express.bodyParser());
app.use(expressValidator);
app.use(express.methodOverride());
app.use(app.router);
});
//display the page for the first time
app.get('/mypage', function(req,res){
res.render('mypage', {
title: 'My Page'
});
});
//handle form submission
app.post('/mypage', function(req,res){
req.assert('name', 'Please enter a name').notEmpty();
req.assert('email', 'Please enter a valid email').len(6,64).isEmail();
var errors = req.validationErrors();
if( !errors){
sendEmail(function(){
res.render('mypage', {
title: 'My Page',
success: true
});
});
}
else {
res.render('mypage', {
title: 'My Page',
errors: errors
});
}
});
So there are three scenarios where my pages is rendered, and each one has access to different local variables:
When the page is loaded for the first time (errors=undefined,success=undefined)
When the form is submitted and there are errors (errors=array,success=undefined)
When the form is submitted and there are no errors (errors=undefined,success=true)
So my main problems are that:
When my Jade page is loaded, it seems to throw an error when I attempt to access a variable that doesn't exist. For example, I want to see if the variable 'success' is set, and if it is I want to hide the form and display a "thanks" message. Is there an easy way to handle a variable in Jade that will either be undefined or a value?
When the form has been submitted and there are validation errors, I want to show an error message (this isn't a problem) but also populate the form with the variables that were previously submitted (e.g. if the user provided a name but no email, the error should reference the blank email but the form should retain their name). At the moment the error message is displayed but my form is reset. Is there an easy way to set the input values of the fields to the values in the post data?
You can fix that by using locals.variable instead of just variable. Also you can use javascript in jade.
-locals.form_model = locals.form_data || {};
I used two ways to solve this problem. The first one is to re-render the view and you pass the req.body as a local. I have a convention that my forms use form_model.value for their field values. This method is works well for simple forms but it starts to breakdown a little when you form is relying on data.
The second method is to pass your req.body to session then redirect to a route that renders the form. Have that route looking for a certain session variable and use those values in your form.
Inside your jade file add error msg and then run your code.
Simply update your jade code with below code:
div#contact-area
ul.errors
if errors
each error, i in errors
li.alert.alert-danger #{error.msg}
form(method='post',action='')
label(for='name') Name:
input(type='text',name='name',id='name')
label(for='email') Email:
input(type='text',name='email',id='email')
input(type='submit',name='submit',value='Submit').submit-button

Resources