Home > OS >  Convert hash-like string from Rails form element to a JS object
Convert hash-like string from Rails form element to a JS object

Time:08-13

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

  • Related