How prevent reload after post request? - javascript

I am trying to make a post request to the server and do something with the response. Things seem to work on the server-side. The problem is that the page reloads upon completion of the response and I cannot do anything with the response.
The common suggestions are using button or preventDefault. These suggestions do not solve the problem: as you can see below, the input type is button (not submit) and preventDefault() on the event does not work.
Does anyone have an idea about what I am missing?
<form id="modelCodeForm">
<label for="codehere"></label>
<div id="modelAreaDiv">
<textarea id="modelArea" cols="60" rows="20">
stuff
</textarea>
<br>
<input id="submitUserModel" type="button" value="run on server">
</div>
</form>
function initializeUserModel(){
let model = document.getElementById("modelArea").value;
fetch('http://localhost:8080/', {
method: 'post',
headers: {'Content-Type': 'text/plain'},
body: model
})
.then(response => response.json())
.then(data => {
console.log(data);
}).then(console.log("received!"))
}

I got to the bottom of this. It turns out that the problem was due to VS Live Server which was detecting a change in the folder and hot-loading the app. The change was due to the backend, in the same folder, saving a log. Really silly thing to miss...

If you want to trigger your function when the form is submitted then you can use the "onsubmit" event listener available on HTML forms.
So you would do onsubmit="initializeUserModel(event)". You pass it the event object so you can call event.preventDefault() in the function and stop the page from reloading.
Change your input to type="submit" (or make it a button of type="submit") or the form submission won't be triggered.
<form id="modelCodeForm" onsubmit="initializeUserModel(event)">
<label for="codehere"></label>
<div id="modelAreaDiv">
<textarea id="modelArea" cols="60" rows="20">Stuff</textarea>
<br />
<input id="submitUserModel" type="submit" value="run on server" />
</div>
</form>
function initializeUserModel(event) {
event.preventDefault();
let model = document.getElementById("modelArea").value;
fetch("http://localhost:8080/", {
method: "post",
headers: { "Content-Type": "text/plain" },
body: model,
})
.then((response) => response.json())
.then((data) => {
console.log(data);
})
.then(console.log("received!"));
}

Related

preventDefault in JS not working when fetch API upload files

I have been trying to create a simple web app where you can post images and a little description. It is for an Instagram Clone project. I am using multer and express on the backend to parse the body and the file, and it works perfectly fine. The problem is on the frontend. I provide a form where you put name, description and the image to upload, then with the Fetch API I send the body to my api on the backend.
I noticed that the page reloads every time I submit the form despite the use of event.preventDefault() and event.stopPropagation() for the form sumbit event.
In addition, I have realised that it does so only when also uploading the file, in fact, if I leave the name and description alone, it doesn't reload the page, whereas if I live the file upload in there it just reloads it.
Is this a bug of the fetch API or am I not considering something?
Here's the code on the frontend that handles the fetch request to my api:
const instaForm = document.getElementById("post-pic");
instaForm.addEventListener("submit", (event) => {
event.preventDefault();
event.stopPropagation();
let instaFormData = new FormData(instaForm);
let url = `${baseUrl}/create-insta`;
fetch(url, {
method: "POST",
body: instaFormData,
})
.then((data) => data.json())
.then((response) => {
instaForm.reset();
console.log(response);
})
.catch((err) => console.error(err));
});
Consider that if I leave the input for the file blank, the page doesn't reload so it is definitely a problem with the file upload.
For now, for debugging my API is sending back a simple "Received" response and nothing else.
HTML Form
<form method="POST" id="post-pic" enctype="multipart/form-data">
<input
type="text"
id="name"
name="name"
placeholder="Enter a name..."
/>
<textarea
name="description"
id="description"
rows="10"
placeholder="A short description..."
></textarea>
<input type="file" id="picture" name="insta" />
<button id="publish" type="submit">Publish</button>
</form>
You should catch the event when clicking on the form button.
Try this:
<form method="POST" id="post-pic" enctype="multipart/form-data">
<input
type="text"
id="name"
name="name"
placeholder="Enter a name..."
/>
<textarea
name="description"
id="description"
rows="10"
placeholder="A short description..."
></textarea>
<input type="file" id="picture" name="insta" />
<button id="publish" type="button">Publish</button>
</form>
JS Script:
const instaForm = document.getElementById("post-pic");
const buttonForm = document.getElementById("publish");
buttonForm.addEventListener("click",(event) => {
event.preventDefault();
let instaFormData = new FormData(instaForm);
let url = `${baseUrl}/create-insta`;
fetch(url, {
method: "POST",
body: instaFormData,
})
.then((data) => data.json())
.then((response) => {
instaForm.reset();
console.log(response);
})
.catch((err) => console.error(err));
});
}
I hope it could help you.
Regards.

Fetching API data with active login

I'm using backend functions on my website to gather data from a separate website.
I can access the data easily with something like...
fetch( DataURL, {"method": "get"} )
.then( httpResponse => httpResponse.json() )
.then( json => { console.log(json); } )
However, there is additional data fields in the json data that aren't visible unless you are logged in.
I'm trying to use a POST fetch to complete the login form, and then send the GET to retrieve the API data afterwards. Have been unsuccessful so far.
fetch( LoginURL, {
"method": "post",
"body": "Username=myusername&Password=mypassword",
"headers": {"Content-Type": "application/x-www-form-urlencoded"}
})
.then( fetch( DataURL, {"method": "get"} )
.then( httpResponse => httpResponse.json() )
.then( json => { console.log(json); })
})
I'm basing the body data on what I see in the form in their source code. Not completely sure if I'm doing that right.
This is a phpBB style website that needs to be logged into.
<form action="./ucp.php?mode=login" method="post" id="navloginform" name="loginform">
<div class="form-group">
<input type="text" placeholder="Username" name="username" size="10" class="form-control" title="Username"/>
</div>
<div class="form-group">
<input type="password" placeholder="Password" name="password" size="10" class="form-control" title="Password"/>
</div>
<div class="form-group">
<div class="checkbox">
<label for="autologin-navbar"><input type="checkbox" name="autologin" id="autologin-navbar" tabindex="4" /> Remember me</label>
</div>
</div>
<input type="hidden" name="redirect" value="./ucp.php?mode=login" />
<button type="submit" name="login" class="btn btn-primary btn-block"><i class="fa fa-sign-in fa-fw" aria-hidden="true"></i> Login</button>
</form>
I'm very clearly doing something wrong.
I think my main issues I'm struggling with are:
Is my guess at how to use their form data correct?
How do I verify the login even worked to begin with?
Do I need to do something special to maintain the login before sending a second fetch?
Just going to share how I got mine working, though may be different for other cases.
--
Go to the website you're trying to login to, and open up your browser development tools.
Select the 'Network' tab, and login.
Find the network entry from your login, and go to the 'Headers' tab. Near the bottom of the entry will show which headers were used. Not all of these are critical. Something like below:
When using your fetch call, create the same headers as needed in the body.
const LoginURL = 'www.ExampleWebpageLogin.com./ucp.php?mode=login';
let form = new FormData();
form.append('username', 'MyUsername');
form.append('password', 'MyPassword');
form.append('login', 'Login');
var headers = { 'method': 'post',
'body': form,
'credentials': 'include'}
await fetch( LoginURL, headers)
.then( loginResponse => {
console.log(loginResponse);
})
In my case, the loginResponse included 2 critical cookies.
user_id - This cookie value remained =1 when not logged in, and was set to my user id once successful.
sid - Most likely session ID, which they use to preserve login session.
For both the login post and future GET requests, 'credentials' are required.

Axios request in Vue.js method working when launched from "mounted()" but not when launched from html <form>

When I authenticate to Laravel Passport backend with Nuxt.js like below, it works and I get a token:
mounted() {
this.axiosGetToken()
}
But, if I run the method with a button in a form, like this:
<form #submit="axiosGetToken()">
<button type="submit">Axios login</button>
</form>
Then, I get the status (canceled) for the Laravel page in the Network tab of my browser developer's tool.
The method looks like this:
axiosGetToken() {
const url = 'http://laravel.test/oauth/token'
const params = {
client_id: 2,
client_secret: 'S0gpcgfIDgbvIHCL3jIhSICAiTsTUMOR0k5mdaCi',
grant_type: 'password',
username: 'me#home.com',
password: '1qaz#WSX'
}
const headers = {
}
this.$axios
.post(url, params, headers)
.then(response => {
// eslint-disable-next-line
console.log(response)
})
.catch(response => {
// eslint-disable-next-line
console.log(response)
})
},
What's wrong with this form ?
You should add prevent modifier :
<form #submit.prevent="axiosGetToken()">
<button type="submit">Axios login</button>
</form>
by default the submit event try to reload the page and searching an action from the back-end in order to run it, so the prevent modifier will prevent the action and allows the running of the js event handler.
As mentioned above, if you use a form with a button of type="submit", when pressing that button, it will use the default client behavior and send a request to the form action URL, adding a prevent will stop that behavior.
Although it is a valid answer, I would suggest adding a #click="axiosGetToken()" on the button.
<form>
<button type="button" #click="axiosGetToken()">Axios login</button>
</form>

I want to submit a form and update the values from the form (Mysql result) without refreshing the whole page

as the title says, I want to submit a form and update the values from the form (Mysql result) without refreshing the whole page.
See Image 1, I want if I press the blue button that the values updated.
Can someone help me?
Thanks.
If you are willing to use newer apis like Promise and fetch, it's super easy and neat.
foo.submit.addEventListener("click", (e) => {
e.preventDefault();
fetch("YOUR_URL", {
method: 'POST',
body: new URLSearchParams({
name: foo.user_name_value,
email: foo.user_mail.value
})
}).then(response => {
return response.json() // assume returning data is in json format, otherwise .text() could be used
}).then(data => {
// deal with return data
}).catch(err => {
// deals with errors
})
})
<form name="foo">
<div>
<label for="name">Name:</label>
<input type="text" id="name" name="user_name">
</div>
<div>
<label for="mail">E-mail:</label>
<input type="email" id="mail" name="user_mail">
</div>
<div>
<button name="submit" type="submit">Send your message</button>
</div>
</form>
Older method XHR shares same idea, but requires a bit more code. You may use axios or so to simplify the process.

Login components in vuejs

I've built a login component with the below code for users to log in the backend is flask and is using flask_login.
const LoginScreen = {
template: `
<div>
<h1>Sign In</h1>
<form id="loginform" class="pure-form">
<fieldset>
<input type="text" name="email" placeholder="email" v-model="logindetails.email"/>
<input type="password" name="password" placeholder="password" v-model="logindetails.password"/>
<button class="pure-button pure-button-primary" v-on:click="login">Login</button>
</fieldset>
</form>
</div>
`,
data: function () {
return {
logindetails:{}
}
},
methods: {
login: function(){
axios.post('/login/',
this.logindetails,
{
headers: {
'Content-type': 'application/json',
'Accept': 'application/json',
'X-CSRF-TOKEN': document.querySelector('#csrftoken').getAttribute('content')
}
}
).then(response => { this.logindetails = {};
this.$router.push({path: '/'});
}
)
.catch(function (error) {
console.log(error);
});
}
}
};
It seems to work correctly (though there are times when it asks me to log in again for seemingly no reason), however the component is putting the submitted form details into the querystring of the url ( example ).
Would anyone be able to tell me what I am doing wrong or, if I am doing this totally incorrectly, point me in the direction of a codebase/guide that is doing logins correctly?
Many thanks in advance.
Take a look at Vue v-on event modifiers to modify the default element behavior.
In your case you can do:
<button #click.prevent="login">Login</button>
Or in the form tag (ensure your submit button is type "submit"):
<form #submit.prevent="login">
with regards to the component is putting the submitted form details into the querystring of the url, the reason is that clicking on the button also trigger submit on the form.
HTML form submits form values to the server.
But normally in JS frameworks like Vue or React, form values are submited through ajax, without any page refresh. So using <form> has not much value here.
2 things you could do in this case
Remove <form> element. it should still works corectly
handle the form submit event.
For e.g.
<form #submit="handleSubmit($event)">
methods: {
handleSubmit:(e) =>{
e.preventDefault();
}
}

Categories

Resources