I'm trying to make an JS object out of all nested form elements in my Rails app. Rails serialises nested form elements like (this is a small sample view of my hidden form elements):
<input name="order[order_rules_attributes][0][quantity]" value="1" type="hidden">
<input name="order[order_rules_attributes][1][quantity]" value="3" type="hidden">
I'm sure that this Hash-like syntax can be converted in JS to an object like:
{
order: {
order_rules_attributes: {
[
{ quantity: 1 },
{ quantity: 3 }
]
}
}
}
But as this is no JSON syntax, nor a split()
-able string I don't know how to move on in this matter.
My question is: how do I convert this html to an JS object like mentioned?
PS: a suggestion has been made to use .serializeArray()
but this jQuery function only serialises form elements as a whole, whereas I'm looking to serialise the "name" attribute too.
CodePudding user response:
This solution tries to be more generic anticipating keys that are integer to be children of an array. That was the tricky part.
const is_array = Array.isArray;
const is_integer = (x) => x == x
var result = {};
document.querySelectorAll("input[type=hidden]").forEach(function(input) {
var name = input.name
var value = input.value
console.log(name, value)
var arr = name.split(/\]?\[|\]\]?/);
arr.pop();
var current = result;
var last_key = null;
var last_obj = null;
for (var i = 0; i < arr.length; i ) {
var key = arr[i]
if (is_integer(key)) {
if (!is_array(last_obj[last_key])) {
last_obj[last_key] = []
current = last_obj[last_key]
}
current[key] = current[key] || {}
} else {
current[key] = current[key] || {}
}
last_obj = current;
current = current[key];
last_key = key;
}
last_obj[last_key] = value;
})
console.log(result);
.as-console-wrapper {
max-height: 100% !important;
}
<input name="order[order_rules_attributes][0][quantity]" value="1" type="hidden">
<input name="order[order_rules_attributes][1][quantity]" value="3" type="hidden">
<input name="order[order_rules_attributes][2][0][another]" value="3" type="hidden">
CodePudding user response:
You could do it like this:
let result = {};
let arr = [...document.querySelectorAll('input')].map(inp=>[inp.getAttribute('name'),inp.getAttribute('value')]);
arr.forEach(function(e){
t = e[0].replaceAll("]","").split("[");
if(!result[t[0]]){
result[t[0]] = {};
result[t[0]][t[1]] = [];
}
let innerObj = {}
innerObj[t[3]] = e[1];
result[t[0]][t[1]].push(innerObj);
});
console.log(result);
<input name="order[order_rules_attributes][0][quantity]" value="1" type="hidden">
<input name="order[order_rules_attributes][1][quantity]" value="3" type="hidden">
NOTE: In HTML
attributes like name
and value
can remain unquoted. Unless if it contains some spaces or any of " ' = < or >
.