1

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.

HJW
  • 342
  • 3
  • 13
  • You can split on `\]?\[|\]\[?` . Have a look at [`[javascript] create nested object from string`](https://stackoverflow.com/search?q=%5Bjavascript%5D+create+nested+object+from+string) too – Felix Kling Aug 12 '22 at 17:52
  • No luck here. `'order[order_rules_attributes][0][quantity]'.split('\]?\[|\]\]')` has no valid response. I already read the posts you mention. But they all consist of strings that are splittable by 1 character. The above name of the html element is more like a hash object that is stringified and because of the `[]` syntax impossible/hard to split and make a nested object out of it. – HJW Aug 12 '22 at 17:57
  • 1
    It's a regular expression (and you copied it incorrectly): `'order[order_rules_attributes][0][quantity]'.split(/\]?\[|\]\[?/)`. – Felix Kling Aug 12 '22 at 18:19
  • Does this answer your question? [Convert form data to JavaScript object with jQuery](https://stackoverflow.com/questions/1184624/convert-form-data-to-javascript-object-with-jquery) – gre_gor Aug 12 '22 at 19:42
  • Unfortunately not. That is serialising all form elements and not the name attribute. – HJW Aug 12 '22 at 20:33

2 Answers2

1

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">
IT goldman
  • 14,885
  • 2
  • 14
  • 28
0

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 >.

XMehdi01
  • 5,538
  • 2
  • 10
  • 34
  • Thank you for your resourceful solution! This definitely helps me further in my solution to be able to get this done with (deeper) nested forms. However, I somewhat expected that this HTML content that is generated by default by @Rails, would have a basic solution. Unfortunately I cannot find anything about this. – HJW Aug 12 '22 at 18:08
  • 1
    This is not generic enough. Will fail for "order[0][something]" – IT goldman Aug 12 '22 at 18:24