I'm trying to write a form_to_json
serialization function in vanilla JS in order to create valid JSON.
But I'm stuck on parsing multiple inputs, all featuring the same name.
Here is my current function, note the comment where I'm stuck.
// gather form elements and serialize inputs
function form_to_json(form_id) {
console.log(`Getting form with ID ${form_id}`);
let obj_form = document.getElementById(form_id);
// Create an object
var obj = {};
// Loop through each field in the form
Array.prototype.slice.call(obj_form.elements).forEach(function(field) {
// Skip some fields we don't need
if (!field.name || field.disabled || ['file', 'reset', 'submit', 'button'].indexOf(field.type) > -1) return;
// Handle multi-select fields
if (field.type === 'select-multiple') {
// Create an array of selected values
var options = [];
// Loop through the options and add selected ones
Array.prototype.slice.call(field.options).forEach(function(option) {
if (!option.selected) return;
options.push(option.value);
});
// If there are any selection options, add them to the object
if (options.length) {
obj[field.name] = options;
}
return;
}
// If it's a checkbox or radio button and it's not checked, skip it
if (['checkbox', 'radio'].indexOf(field.type) > -1 && !field.checked) return;
console.log(`${field.name}: ${field.value}`);
// Add the value to the object
if (field.name in obj) {
// NOT SURE WHAT TO DO HERE
obj[field.name].push(field.value);
} else {
obj[field.name] = field.value;
}
});
// Return the object
return obj;
}
Here is a screenshot showing a visual of how the inputs are created by the end user.
With my current function code, I'm obviously only getting the last set of values for each redundant field name.
The front end allows user to dynamically add "criteria" rows (basically a set of those 3 inputs all named identically between each row).
My attempt was to check if the key already existed in the object, and if so, /do something/ but I can't work out what that something is.
Regardless of the fact that the OP actually does not want to create JSON
(which is a string-based data-format), but wants to create unified form-data in a custom way, any solution in first place should have been based at the usage of the FormData
Web API, a form-data's entries
list and a single reduce
task.
A FormData
instance provides access to all of a form's relevant control/element-values, actually already filtered in the way the OP is looking for, which only leaves the task of collecting the values of equally named field-names into arrays, thus reducing the entries array (key-value pairs) into an object based data-structure.
The entire code becomes shorter and better readable.
function getUnifiedFormData(elmForm) {
return [
...new FormData(elmForm).entries()
]
.reduce((result, [key, value]) => {
if (Object.hasOwn(result, key)) {
if (Array.isArray(result[key])) {
result[key].push(value);
} else {
result[key] = [result[key], value];
}
} else {
result[key] = value;
}
return result;
}, {});
}
document
.forms['my-form']
.addEventListener('submit', evt => {
evt.preventDefault();
const data = getUnifiedFormData(evt.currentTarget);
console.clear();
console.log({ data });
});
body { margin: 0; }
form { width: 29%; }
label { float: left; margin: 2px 4px; }
select { clear: left; margin: 2px 0; padding: 2px 10px; }
[type="text"] { margin: 4px 0; }
[type="text"], select, label > span { display: block; }
.as-console-wrapper { left: auto!important; width: 70%; min-height: 100%; }
<form id='my-form'>
<input type="text" name='text-values' />
<input type="text" name='text-values' />
<input type="text" name='text-values' />
<label>
<span>red</span>
<input type="checkbox" name="selected-colors" value="red" />
</label>
<label>
<span>yellow</span>
<input type="checkbox" name="selected-colors" value="yellow" />
</label>
<label>
<span>green</span>
<input type="checkbox" name="selected-colors" value="green" />
</label>
<select name="picked-fruits" multiple>
<option>Apple</option>
<option>Orange</option>
<option>Banana</option>
</select>
<input type="text" name='other' value="other value" disabled />
<input type='submit' name="submit" value="submit" />
<input type='reset' name="reset" value="reset" />
<input type='button' name="button" value="button" />
</form>