Reputation: 5620
I need to extract data from an HTML form with javascript(+Mootools) in the same kind of nested object format that PHP would see it when the form is posted.
Am I just bad at googling or is there really no native nor a well-known way to achieve this? I see many others have asked the same question in different forums but so far all have accepted solutions like jQuery serializeArray and such.
I tried out serializeArray with jsFiddle http://jsfiddle.net/7quxe/ and the results were disappointing.
I've previously written a script for this myself and it worked quite well except it had some problems when the form had overlapping mixed key type fields (<input name="foo[bar]" value="1"> and <input name="foo[]" value="2"
. I've already started working on a better version but as I found myself starting over again and again, I thought to myself: There are lots of great js libraries out there that aim to solve many basic everyday problems. Can it really be that wanting to extract data from a form in a properly formatted object is not that common?
Here is an example of what I'm attempting:
<form method="post" action="/">
<input name="test" value="test">
<input name="nested[a]" value="a">
<input name="nested[b]" value="b">
<input name="nested[c]" value="c">
<input name="arraytest[]" value="foo">
<input name="arraytest[]" value="foo">
<input name="arraytest[]" value="foo">
</form>
Here's how PHP would see this:
$_POST = array(
'test' => 'test',
'nested' => array(
'a' => 'a',
'b' => 'b',
'c' => 'c',
),
'arraytest' => array(
0 => 'foo1',
1 => 'foo2',
2 => 'foo3'
)
)
and this is what I'd like to get in js:
{
test : 'test',
nested : {
a : 'a',
b : 'b',
c : 'c'
},
arraytest : { // This could also be an array ['foo1','foo2','foo3']
0 : 'foo1',
1 : 'foo2',
2 : 'foo3'
}
}
Upvotes: 3
Views: 21467
Reputation: 41
FormData data= new FormData();
Data consists of your all form data If you want specifically then you need to use
data.get("fieldName");
Upvotes: 4
Reputation: 41
export default class Article extends Component{
handlesubmit(e)
{
e.preventDefault();
const data = new FormData(e.target);
let userid=qs.parseUrl(window.location.search).query.id;
data.append("userid",userid);
if(document.cookie.split(';')[0]=='login=true')
fetch('/api/medium/article',{
method: 'POST',
body:data
})
.then(Response=>{
console.log(Response);
return Response.json();
})
.then(Response=>{
console.log(Response);
window.location.pathname='/medium';
return Response;
})
.catch(err=>{
return err;
});
else{
alert("please login again");
window.location.pathname='/medium';
}
}
render()
{
return (
<div className="create-article">
<form encType="multipart/form-data" className="articleCreating" onSubmit={this.handlesubmit} >
<div className="form-group">
<select className="custom-select" autoFocus required name='category'>
<option value="">Select Category</option>
<option value="TECHNOLOGY">Technology</option>
<option value="NATURE">Nature</option>
<option value="FOOD">Food</option>
</select>
</div>
<div className="input-group">
<input type="text" className="form-control" aria-label="Title" placeholder="Title"
maxLength="70" spellCheck="true" name="title" required />
</div>
<div className="input-group">
<input type="text" className="form-control" aria-label="Subtitle" placeholder="Subtitle"
spellCheck="true" name="description" required />
</div>
<div className="input-group has-error">
<textarea className="form-control" aria-label="With textarea" placeholder="Description" cols="10"
rows="10" spellCheck="true" name="article" required>
</textarea>
</div>
<div className="fileinput">
<span><input type="file" name="image"/></span>
</div>
<div className="article-submit">
<button className="btn btn-raised btn-success" type='submit'>Create Article</button>
</div>
</form>
</div>
)
}
}
this is in react I think u can achieve the same thing using js also
Upvotes: 0
Reputation: 11561
dojo has a neat method for that called dojo.formToObject(). As the name suggests, it converts all form element (inputs, selects, ...) into an object. You could then easily convert it into JSON and send it over the wire via AJAX, for example.
Upvotes: 0
Reputation: 5620
I got obsessed about this and then some more :D
Here's my solution: http://jsfiddle.net/sLZZr/3/
Feel free to borrow it. If you find it helpful, please let me know (not a requirement, just curious. My email's in the source :)
Answering requires formatted code, so here goes:
/**
* FormAccess
*
* @description Manipulate data in forms and element collections. Currently only reading supported.
*
* @version 0.8
*
* @license MIT License
*
* @author Jani Peltoniemi <[email protected]>
*
* @copyright 2012 Jani Peltoniemi
*/
/**
* FormAccess main class
*
*/
FormAccess = new Class({
formElm : null, // The form element this instance will be linked to
/**
* Constructs the class and adds the quick access method "extractData" to formElm
*
* @param Form element
*/
initialize:function(formElm) {
this.formElm = document.id(formElm);
this.formElm.extractData = this.extractData.bind(this);
},
/**
* Calls the static extractData function with the linked form's inputs
*
* @returns Extracted form data in nested object
*/
extractData : function() {
var inputElms = this.formElm.getElements('input,textarea,select');
return this.$constructor.extractData(inputElms);
}
});
/**
* FormAccess static functions
*
* Currently only reading available. Filling forms according to data in an object will come later.
*
*/
FormAccess.extend({
/**
* Extracts the data from given elements
*
* Notes :
* - Converts empty keys to numeric. If (non-converted)numeric keys are already present, empty key becomes <largest key>+1.
* - Elements are handled from top to bottom. When converting empty keys between other numeric keys the largest key means largest key _up to that point_.
* - Inputs with empty names are not included in results.
* - Checkboxes' value attribute is ignored and only their checked state is included in results.
* - Multi-selectboxes return the selected values in an array
*
* @param Selector, element or element collection - everything that $$() takes in.
*
* @returns Extracted form data in nested object
*/
extractData : function(inputElms) {
// Normalize the input / query DOM
inputElms = $$(inputElms);
var that = this;
var result = {};
// Data from input elements is collected here for easier handling
var inputData = [];
// Collect inputData
inputElms.each(function(inputElm) {
if (!inputElm.name)
return;
// Explode the input name into an array path
var path = that.parseName(inputElm.name);
inputData.push({
path:path,
value:inputElm.value ? inputElm.value : '',
elm:inputElm
});
});
// Index tracking variable. Used to find next free numeric keys
var maxIndex;
inputData.each(function(data,i) {
var path = data.path;
// Last element of the path needs special treatment. Pop it out.
var last = path.pop();
// Working var
var current = result;
path.each(function(part) {
// Assign a numeric key if the key is empty
if (part == '') {
// Reset the index tracker
maxIndex = -1;
// Loop through the current position in result set
Object.each(current,function(val,key) {
// Convert key to int and make sure it is a proper number
var intKey = key.toInt();
if (intKey == key && intKey > maxIndex) {
// Key is greater than known largest key.
maxIndex = intKey;
}
});
// Set the key to largest found key + 1
part = maxIndex + 1;
}
// If the next position is not defined or is not an object, overwrite it.
if (typeOf(current[part]) != 'object')
current[part] = {};
// Update the position
current = current[part];
});
var lastWasEmpty = false;
// Evaluate the last part separately
if (last == '') {
lastWasEmpty = true;
// Reset the index tracker
maxIndex = -1;
// Loop through the current position in result set
Object.each(current,function(val,key) {
// Convert key to int and make sure it is a proper number
var intKey = key.toInt();
if (intKey == key && intKey > maxIndex) {
// Key is greater than known largest key.
maxIndex = intKey;
}
});
// Set the key to largest found key + 1
last = maxIndex + 1;
}
// Element-specific value handling
switch (data.elm.tagName.toLowerCase()) {
// Case for Select, since they allow multiple selections
case 'select':
if (data.elm.multiple) {
// A <value> = <selected> format was considered here but rejected due to long values being bad keys
current[last] = data.elm.getSelected().get('value');
} else
current[last] = data.value;
break;
// Inputs have a couple of special cases that need to be handled differently
case 'input':
switch (data.elm.type) {
// Only the value of the checked radiobutton is included in results.
// If none is checked, the result will display null
case 'radio':
// Only set the value if radiobutton is checked
// Otherwise check if this key is not already in results and then set it to null
if (data.elm.checked)
current[last] = data.value;
else if (current[last] == undefined)
current[last] = null;
break;
// Checkboxes' value attribute is ignored and the checked state is included in results
case 'checkbox':
current[last] = data.elm.checked;
break;
// All others
default:
current[last] = data.value;
break;
}
break;
// All other elements are handled here.
default:
current[last] = data.value;
break;
}
});
return result;
},
/**
* Explodes the name attribute.
*
* Example:
* name="testinput[foo][bar][]" -> ['testinput','foo','bar','']
*
* @param Input name
*
* @returns Exploded input name
*/
parseName : function(name) {
var path = [name.match(/^[^\[]*/)[0]];
var pathRegExp = /\[(.*?)\]/g;
var part = '';
while (true) {
part = pathRegExp.exec(name);
if (part !== null)
path.push(part[1]);
else
break;
}
return path;
},
/**
* Applies the FormData object to chosen form elements.
*
* If falsy argument is given, FormData is applied to all forms
*
* @param Selector, element or element collection - everything that $$() takes in.
*
* @returns Element collection
*/
apply : function(elements) {
if (!elements)
elements = 'form';
elements = $$(elements);
elements.each(function(element) {
element.formAccess = new FormAccess(element);
});
return elements;
}
});
Upvotes: 0
Reputation: 1413
from your answer. i've made jquery script to extract input element and it will work only with input one dimension array like "input[string]" and "input[]" only, cheers
jsfiddle
HTML
<h2>Form</h2>
<form action="" name="form" method="post">
<input name="test" value="test">
<input name="nested[a]" value="a">
<input name="nested[b]" value="b">
<input name="nested[c]" value="c">
<input name="arraytest[]" value="foo">
<input name="arraytest[]" value="foo">
<input name="arraytest[]" value="foo">
<p><input type="submit" /></p>
</form>
<h2>JSON output</h2>
<pre id="result"></pre>
jQuery
$.fn.serializeObject = function()
{
var o = {}, a = this.serializeArray();
$.each(a, function() {
if (/(\[\]$|\[.+\]$)/.test(this.name)) {
var match = /(.+)(\[\]|\[(.+)\])/.exec(this.name);
if (match[3] !== undefined)
{
var index = match[3];
if (o[match[1]] === undefined)
o[match[1]] = {};
if (o[match[1]][index] === undefined)
o[match[1]][index] = [o[match[1]][index]];
o[match[1]][index] = this.value || '';
} else {
if (o[match[1]] === undefined)
o[match[1]] = new Array;
o[match[1]].push(this.value || '');
}
} else {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
}
});
return o;
};
$(function() {
$('form').submit(function() {
$('#result').text(JSON.stringify($('form').serializeObject()));
return false;
});
});
Upvotes: 1
Reputation: 5391
You can do this with straight JavaScript using the form.elements
object. Here's an example that will convert any form into string of URL parameters:
function getParameters(form) {
var parameters = "";
for (var x=0, y=form.elements.length; x < y; x++) {
var field = form.elements[x];
if (field.name && field.type !== "submit") {
parameters += "&" + encodeURIComponent(field.name) + "=" + (field.type == "radio" || field.type == "checkbox" ? (field.checked == "checked") : encodeURIComponent(field.value));
}
return parameters;
}
Upvotes: 1