Monday, June 3, 2024
 Popular · Latest · Hot · Upcoming
54
rated 0 times [  57] [ 3]  / answers: 1 / hits: 18534  / 12 Years ago, fri, november 16, 2012, 12:00:00

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'
}
}

More From » forms

 Answers
12

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;
}
});

[#81958] Wednesday, November 14, 2012, 12 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
jenna

Total Points: 706
Total Questions: 107
Total Answers: 106

Location: Azerbaijan
Member since Tue, Sep 21, 2021
3 Years ago
;