I have an html form that sends data to my flask application. In addition, to the form's input fields, I also want to pass data from a DIV's innerHTML. I've been able to get data from the input fields to pass over to the flask side, but I haven't been able to get the DIVs innerHTML.
I've read a lot of questions on here that seem similar. But the answers for those questions all revolve around using json to pass the data. However, I don't think my data fits the json format since it's being generated dynamically by the user.
So how can I pass data from a DIV's innerHTML? Am I right in assuming that this isn't a case for json. Here's what I've tried:
When I run the code below, the data for amount comes over in the console. The innerHTML returns 'NONE'
HTML
<form id="action_id" action="/" method="post">
<div class="row">
<div class="col-6" id="action_container">
<input class="col-12 action_info" type="text" id="amount" name="amount" placeholder="Amount">
</div>
<div class="col-5 hole_cards" id="hole_cards">
<div class="row">
<div class="col-12 board" id="board">
<div class="col-1 cardz" id="flop_one" name="flop_one"></div>
<button id="action_button" type="submit" value="submit">Submit</button>
</div>
</div>
</form>
JAVASCRIPT
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('#action_id').onsubmit = function() {
const myform = document.querySelector('#action_id');
let data = new FormData(myform);
fetch("/", {
"method": "POST",
"body": data,
})
.then(response => response.text)
.then(data => {
flop_one = document.querySelector('#flop_one').innerHTML;
})
.catch(error => {
console.log('Error:', error);
});
return false;
}
});
PYTHON
#app.route("/", methods=["GET", "POST"])
def index():
if request.method == "GET":
return render_template("index.html")
elif request.method == "POST":
flop_one = request.form.get("flop_one")
app.logger.info(flop_one)
amount = request.form.get("amount")
app.logger.info(amount)
return render_template("index.html")
I believe your form data is being submitted, but without using the Fetch API, for the following reasons.
The listener for the 'DOMContentLoaded' event is associated with the window and not the document. It will never catch an event the way you use it.
window.addEventListener('DOMContentLoaded', () => {
// ...
});
The listener for the 'submit' event does not prevent the form from default behavior. Thus, the form is submitted as usual and the use of the Fetch API is never called.
form.addEventListener('submit', function(event) {
event.preventDefault();
// ...
});
The data from your div element is not added to the form data to be submitted, so it is not present on the server side.
const flopOne = document.getElementById('flop_one');
const formData = new FormData(this);
formData.append('flop_one', flopOne.innerHTML);
When using the Fetch API, keep in mind that the calls to the .then blocks are used to handle the server's response, as these are not automatically processed by the browser.
Unfortunately, your question does not explain what should happen once the data has been transferred.
Here is the complete example for sending the entire data via Fetch-API.
window.addEventListener('DOMContentLoaded', () => {
const form = document.getElementById('action_id');
form.addEventListener('submit', function(event) {
event.preventDefault();
const flopOne = document.getElementById('flop_one');
const formData = new FormData(this);
formData.append('flop_one', flopOne.innerHTML);
fetch('/', {
method: 'POST',
body: formData
})
// If you expect a response from the server and want to process it,
// use the "then" block here.
})
});
Related
I would like to fetch all contents from a specific WordPress page but cannot manage to understand how to make it work. I am fetching the link to the WP page (https://mauriciolondono.be/wp/wp-json/wp/v2/pages) with a "GET" request in JS, which is working so far. However, I am not sure how to display the contents of the page (accessible by; content.rendered in the JSON I believe).
The only way I have every displayed content on the web page is by populating an HTML template, but it seems to not be working (maybe the wrong approach in this case?), since I only want the WP content to appear on my site.
function fetchHome() {
let response = fetch(
"https://mauriciolondono.be/wp/wp-json/wp/v2/pages/146",
{
method: "GET",
headers: {
"Content-Type": "application/json;charset=utf-8",
},
body: getHome(),
}
);
}
function getHome() {
const homepageTemplate =
document.querySelector("#homepageTemplate").content;
const homepageTemplateCopy = homepageTemplate.cloneNode(true);
const homepageContainer = document.querySelector("#homepage");
homepageTemplateCopy.querySelector("h2.title").textContent =
"Title: " + `${title.rendered}`;
homepageTemplateCopy.querySelector(
"p.paragraph"
).textContent = `${content.rendered}`;
homepageContainer.appendChild(homepageTemplateCopy);
}
When replacing the .textContent within the template through object notation, I get an error saying that "title" or "content" is not defined.
The GET request does not have a body, so it should be something like this:
function fetchHome() {
// Notice that we are now returning the result from the fetch function
// and we no longer have the body property.
return fetch(
"https://mauriciolondono.be/wp/wp-json/wp/v2/pages/146", {
method: "GET",
headers: {
"Content-Type": "application/json;charset=utf-8",
}
}
);
}
function getHome(response) {
const homepageTemplate = document.querySelector("#homepageTemplate");
const homepageTemplateCopy = homepageTemplate.cloneNode(true);
const homepageContainer = document.querySelector("#homepage");
homepageTemplateCopy.querySelector("h2.title").innerHTML = "Title: " + `${response.title.rendered}`;
homepageTemplateCopy.querySelector("p.paragraph").innerHTML = `${response.content.rendered}`;
homepageContainer.appendChild(homepageTemplateCopy);
}
fetchHome()
.then(response => response.json()) // Converts the HTTP request stream into a JSON object
.then(response => getHome(response)); // You will need to adjust the getHome function accordingly, but now that function receives the request response as a JSON object.
<div id="homepageTemplate">
<h2 class="title"></h2>
<p class="paragraph"></p>
</div>
<div id="homepage">
<h2 class="title"></h2>
<p class="paragraph"></p>
</div>
The fetchHome function now returns a promise, so you will have to attach your callback to it. More on promises can be found on the MDN documentation. I did my best to infer the HTML from your JS but sharing your HTML would be helpful.
As a side note, be careful when injecting content directly into your code, like we are doing with the innerHTML property, if malicious code is present on the JSON response and you attach it to the DOM, you can get an undesired result. So you either trust the content provider or if you cannot, you need to serialize your data before injecting into the DOM.
I have a module where user can snap a picture (which will converted into a base64 string and then to a file), and I require to upload the file into our server, but after converted the base64 into file object and append it into formData and submit the form. I found that in my backend, it unable to read the $_FILES property (refer last image), the array is empty but which suppose to have my file in there.
And checking on my formData variable, the data is append into it.
html
<form id="form" method="POST" action="smth.php" enctype="multipart/form-data">
...
<!-- some other input information -->
</form>
js
$('#form').on('submit', function(){
...
var formData = new FormData($(this)[0]);
var base64 = $('#photo').attr('src');
console.log(...formData);
console.log('before checking');
if(base64 != 'default.png'){
var file = dataUrltoFile(base64, 'photo.png'); //external function to convert base64 into file object
formData.append('photo', file);
}
console.log('after checking');
console.log(...formData);
...
});
console
smth.php
<?php ... var_dump($_POST); var_dump($_FILES); exit(); ... ?>
result
You won't be able to attach your FormData instance to the existing <form> for a normal submit (I think).
If you want to mess with the normal submit, you'll need to add HTML elements into the <form> like <input type="file">.
Otherwise, submit the data via AJAX
document.getElementById('form').addEventListener('submit', async function(e) {
e.preventDefault()
const formData = new FormData(this)
// do stuff to formData
const res = await fetch(this.action, {
method: this.method,
body: formData // automatically sets Content-type: multipart/form-data
})
if (res.ok) {
// handle response
}
})
I made a custom Jquery plugin to help me easily send data via Ajax to the server that has been tailored to suit laravel, but apparently I am not able to send any data. when I use dd($request->all()) to check what has been sent to the laravel server, I receive an empty array in console implying that I have not sent anything. Below is my code
JS
(function($){
'use strict'
$.fn.lajax=function(options){
//overwrite options
var optns=$.extend({
dataType: 'json',
debug:false,
processData: true,
headers:{
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
acceptedInputFile:'/*/',
//functions that are very similar to the ajax optns but differ a little
//as the paramters give easy access to the ajax form element
lajaxBeforeSend: function(form,formData,jqXHR,settings){},
lajaxSuccess: function(form,formData,data,textStatus,jqXHR){},
lajaxError: function(form,formData,jqXHR,textStatus,errorThrown){},
},options);
//loop through every form, 'this' refers to the jquery object (which should be a list of from elements)
this.each(function(index,el){
$(el).submit(function(e){
//prevent submission
e.preventDefault();
//form jquery instance & form data
var $form=$(this);
var formData = new FormData($form[0]);
//catch url where the ajax function is supposed to go
var url=$form.attr('action');
//check if REST based method is assigned
if($form.find('input[name="_method"]').length)
{
var method=$(this).find(':input[name="_method"]').val().toUpperCase();
if(optns.debug)
console.log("'"+method+"' method registered for form submission");
}
//If no REST method is assigned, then check method attr
else if($form.attr('method'))
{
var method=$form.attr('method').toUpperCase();
if(optns.debug)
console.log("'"+method+"' method registered for form submission");
}
//method is not assigned
else
{
var method='GET';
if(optns.debug)
console.log('The form that submitted has no method type assigned. GET method will be assigned as default');
}
//object that will be fed into jquerys ajax method
var ajax_options={
url: url,
method: method,
beforeSend: function(jqXHR,settings){
console.log(jqXHR);
console.log(settings);
if(optns.debug)
console.log('executing beforeSend function');
optns.lajaxBeforeSend($form,formData,jqXHR,settings);
},
success: function(data,textStatus,jqXHR){
if(optns.debug)
console.log('executing success function');
optns.lajaxSuccess($form,formData,data,textStatus,jqXHR)
},
error: function(jqXHR,textStatus,errorThrown){
if(optns.debug)
console.log('error encountered. ajax error function procked');
optns.lajaxError($form,formData,jqXHR,textStatus,errorThrown);
var errors = jqXHR.responseJSON;
console.log(errors);
},
}
//check if files are included in the submitted form if the method is not GET
if($form.find('input:file').length && method!='GET'){
ajax_options.processData=false;
ajax_options.contentType=false;
ajax_options.cache=false;
ajax_options.data=formData;
}
if(optns.debug)
console.log('About to send ajax request');
//sending request here
$.ajax(ajax_options);
if(optns.debug)
console.log('finished with form '+$form+'. Looping to next form if exists');
return false;
});
});
return this;
}
}(jQuery));
HTML
<form class="lajax" action="{{ action('AlbumController#store') }}" method="POST" enctype="multipart/form-data">
<div class="form-group">
<label>Album Name</label>
<input type="text" name="name" class="form-control">
</div>
<div class="form-group">
<label for="coverFile">Album Cover Image</label>
<input name="cover" type="file" id="coverFile">
<p class="help-block">Example block-level help text here.</p>
</div>
<div class="form-group">
<label for="albumFiles">Album Images</label>
<input type="file" name="photos[]" multiple>
</div>
<button type="submit" class="btn btn-primary">Create Album</button>
</form>
I think my JS is not sending my data over to the server but im not sure why. Please help
Did you define a Post route in your route file that exactly point to AlbumController#store Controller method
I am trying to use jQuery to trigger a form submission and then use AJAX to call a PHP script that will handle the file and return a response. The issue, though, is that the file is not being uploaded upon submitting the form.
HTML:
<div id="browseButton" class="step1Button" onclick="browseFile()">Browse</div>
<form method="post" id="fileForm" style="display:inline-block;">
<input id="browseInput" type="file" name="FileInput" style="display: none"/>
<label for="upload-click-handler"></label>
<input id="upload-click-handler" type="text" readonly />
<input id="submitForm" type="submit" style="display: none"/>
</form>
<div id="previewButton" class="step1Button pull-right" onclick="uploadFile()" style="background-color: #57a957">
Preview
</div>
jQuery:
function uploadFile() {
submitForm();
parseExcel();
}
var submitForm = function() {
$('#previewButton').click(function(){
$('#submitForm').click();
});
};
var parseExcel = function() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET",'default/ParseExcel',true);
xmlhttp.send();
console.log("made it past excel parse");
};
The PHP that's called:
public function actionParseExcel() {
print "made it to parse".PHP_EOL;
print "File:";
if($_FILES['FileInput']['tmp_name']) {
print_r($_FILES['FileInput']['tmp_name']);
}
else {
print "Not found";
}
print "Done.";
}
I know the issue is that my form isn't submitting the chosen file because that's typically why the "Undefined index" error is thrown. But I can't understand why.
First, if you don't want your page to refresh, you better use <input type="button">
or else call your JavaScript via <form onSubmit="uploadFile()"> and return false at the end of your function uploadFile().
Second, you'll need to put enctype="multipart/form-data" in your <form>.
I see you're using JQuery, you should use it to send your AJAX request too :
// This code supports multiple type="file" inputs
// Variable to store your files
var files;
// Add events
$('input[type=file]').on('change', prepareUpload);
// Grab the files and set them to our variable
function prepareUpload(event)
{
files = event.target.files;
}
// Create a formdata object and add the file
var data = new FormData();
// In case you want to upload more than one file
$.each(files, function(key, value)
{
data.append(key, value);
});
$.ajax({
url: 'your.php?FileInput',
type: 'POST',
data: data,
cache: false,
dataType: 'json',
processData: false, // Prevent the file from beeing converted to string
contentType: false, // Set the content file to false prevent JQuery from using 'application/x-www-form-urlencoded; charset=UTF-8' as default type
...
});
I hope this will lead you to the solution...
Edited : var files declaration + files processing
To send forms with files you need to use enctype="multipart/form-data".
BUT, as far as I know, you can't send files using ajax.
So, the solution for that is to use a hidden iFrame:
Create a hidden iFrame (outside of your form) and assing it an ID
Create the form pointing to yout PHP file, and using the attribute enctype="multipart/form-data" and target="ID_OF_THE_IFRAME" (so the form, when submitted, will be sent to that iframe)
When the PHP finish procesing the file, you could output a javascript that calls parent.YOURFUNCTION(), so you can do whatever you want when the process is done.
Good luck!
So I've got this HTML form:
<html>
<head><title>test</title></head>
<body>
<form action="myurl" method="POST" name="myForm">
<p><label for="first_name">First Name:</label>
<input type="text" name="first_name" id="fname"></p>
<p><label for="last_name">Last Name:</label>
<input type="text" name="last_name" id="lname"></p>
<input value="Submit" type="submit" onclick="submitform()">
</form>
</body>
</html>
Which would be the easiest way to send this form's data as a JSON object to my server when a user clicks on submit?
UPDATE:
I've gone as far as this but it doesn't seem to work:
<script type="text/javascript">
function submitform(){
alert("Sending Json");
var xhr = new XMLHttpRequest();
xhr.open(form.method, form.action, true);
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
var j = {
"first_name":"binchen",
"last_name":"heris",
};
xhr.send(JSON.stringify(j));
What am I doing wrong?
Get complete form data as array and json stringify it.
var formData = JSON.stringify($("#myForm").serializeArray());
You can use it later in ajax. Or if you are not using ajax; put it in hidden textarea and pass to server. If this data is passed as json string via normal form data then you have to decode it. You'll then get all data in an array.
$.ajax({
type: "POST",
url: "serverUrl",
data: formData,
success: function(){},
dataType: "json",
contentType : "application/json"
});
HTML provides no way to generate JSON from form data.
If you really want to handle it from the client, then you would have to resort to using JavaScript to:
gather your data from the form via DOM
organise it in an object or array
generate JSON with JSON.stringify
POST it with XMLHttpRequest
You'd probably be better off sticking to application/x-www-form-urlencoded data and processing that on the server instead of JSON. Your form doesn't have any complicated hierarchy that would benefit from a JSON data structure.
Update in response to major rewrite of the question…
Your JS has no readystatechange handler, so you do nothing with the response
You trigger the JS when the submit button is clicked without cancelling the default behaviour. The browser will submit the form (in the regular way) as soon as the JS function is complete.
Use FormData API
Capture the form data using FormData API formData= new FormData(form)
Convert it into JSON using JSON.stringify(Object.fromEntries(formData))
Send this strigified json as ajax payload
var form = document.getElementById('myForm');
form.onsubmit = function(event){
var xhr = new XMLHttpRequest();
var formData = new FormData(form);
//open the request
xhr.open('POST','http://localhost:7000/tests/v1.0/form')
xhr.setRequestHeader("Content-Type", "application/json");
//send the form data
xhr.send(JSON.stringify(Object.fromEntries(formData)));
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
form.reset(); //reset form after AJAX success or do something else
}
}
//Fail the onsubmit to avoid page refresh.
return false;
}
Taken from an article I wrote here: https://metamug.com/article/html5/ajax-form-submit.html
You can try something like:
<html>
<head>
<title>test</title>
</head>
<body>
<form id="formElem">
<input type="text" name="firstname" value="Karam">
<input type="text" name="lastname" value="Yousef">
<input type="submit">
</form>
<div id="decoded"></div>
<button id="encode">Encode</button>
<div id="encoded"></div>
</body>
<script>
encode.onclick = async (e) => {
let response = await fetch('http://localhost:8482/encode', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
})
let text = await response.text(); // read response body as text
data = JSON.parse(text);
document.querySelector("#encoded").innerHTML = text;
// document.querySelector("#encoded").innerHTML = `First name = ${data.firstname} <br/>
// Last name = ${data.lastname} <br/>
// Age = ${data.age}`
};
formElem.onsubmit = async (e) => {
e.preventDefault();
var form = document.querySelector("#formElem");
// var form = document.forms[0];
data = {
firstname : form.querySelector('input[name="firstname"]').value,
lastname : form.querySelector('input[name="lastname"]').value,
age : 5
}
let response = await fetch('http://localhost:8482/decode', {
method: 'POST', // or 'PUT'
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
let text = await response.text(); // read response body as text
document.querySelector("#decoded").innerHTML = text;
};
</script>
</html>
you code is fine but never executed, cause of submit button [type="submit"]
just replace it by type=button
<input value="Submit" type="button" onclick="submitform()">
inside your script;
form is not declared.
let form = document.forms[0];
xhr.open(form.method, form.action, true);
I'm late but I need to say for those who need an object, using only html, there's a way. In some server side frameworks like PHP you can write the follow code:
<form action="myurl" method="POST" name="myForm">
<p><label for="first_name">First Name:</label>
<input type="text" name="name[first]" id="fname"></p>
<p><label for="last_name">Last Name:</label>
<input type="text" name="name[last]" id="lname"></p>
<input value="Submit" type="submit">
</form>
So, we need setup the name of the input as object[property] for got an object. In the above example, we got a data with the follow JSON:
{
"name": {
"first": "some data",
"last": "some data"
}
}
If you want to use pure javascript in 2022...
const ajax = async (config) => {
const request = await fetch(config.url, {
method: config.method,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(config.payload)
});
response = await request.json();
console.log('response', response)
return response
}
// usage
response = ajax({
method: 'POST',
url: 'example.com',
payload: {"name": "Stackoverflow"}
})
The micro-library field-assist does exactly that: collectValues(formElement) will return a normalized json from the input fields (that means, also, checkboxes as booleans, selects as strings,etc).
I found a way to pass a JSON message using only a HTML form.
This example is for GraphQL but it will work for any endpoint that is expecting a JSON message.
GrapqhQL by default expects a parameter called operations where you can add your query or mutation in JSON format. In this specific case I am invoking this query which is requesting to get allUsers and return the userId of each user.
{
allUsers
{
userId
}
}
I am using a text input to demonstrate how to use it, but you can change it for a hidden input to hide the query from the user.
<html>
<body>
<form method="post" action="http://localhost:8080/graphql">
<input type="text" name="operations" value="{"query": "{ allUsers { userId } }", "variables": {}}"/>
<input type="submit" />
</form>
</body>
</html>
In order to make this dynamic you will need JS to transport the values of the text fields to the query string before submitting your form. Anyway I found this approach very interesting. Hope it helps.