Skip to content
Advertisement

Create HTML form from any nested JS objects

I want to dynamically create an html form from any random nested js objects. For eg,

{
      "name":"Ram",
      "age":27,
      "vehicles": {
         "car":"limousine",
         "bike":"ktm-duke",
         "airlines":{
            "lufthansa" : "Air123",
             "British airways" : "Brt707"
         }
      }
   }

I don’t want to use online converter. Also I don’t want to look like it as html table. Rather it should be as html form in parent-child structure Can someone provide me solution in javascript code

I want my html form should look like this.. enter image description here

HTML Code:-

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <title>Nested Fieldset Example</title>
    <style type="text/css">
      fieldset fieldset {
        width: 45%;
        float: left;
      }
      #submit {
        margin-bottom: 10px;
        float: right;
      }
    </style>
  </head>
  <body>
    <form>
        <p>
            <label for="name">name:</label>
            <input type="text" name="name" id="name" placeholder="Ram"/>
        </p>
        <p>
            <label for="age">age:</label>
            <input type="number" name="age" id="age" placeholder="27"/>
        </p>
      <fieldset>
        <legend>-vehicles</legend>
        <p>
            <label for="car">car:</label>
            <input type="text" name="car" id="car" placeholder="limousine"/>
        </p>
        <p>
            <label for="bike">bike:</label>
            <input type="text" name="bike" id="bike" placeholder="ktm-duke"/>
        </p>
        <fieldset>
          <legend>-airlines</legend>
            <p>
              <label for="lufthansa">lufthansa:</label>
              <input type="text" name="lufthansa" id="lufthansa" placeholder="Air123"/>
            </p>
            <p>
              <label for="British airways">British_airways:</label>
              <input type="text" name="British airways" id="British airways" placeholder="Brt707"/>
            </p>
        </fieldset>

      </fieldset>
    
    </form>
  </body>
</html>

   
  

Advertisement

Answer

Obviously, you will need to iterate on the object’s elements and create inputs with labels for each. Because the object can be nested, you will need to apply a recursive function.

You render the function that creates a group of inputs for one level of the object structure—I named it createFormGroup—recursive by making it call itself if the value is an object.

// simplified demonstration
const createFormGroup = (key, obj) => {
  // iterate on the object’s entries
  for (const [key, value] of Object.entries(obj)) {
    // call recursively if a value is an object itself
    if(typeof value === 'object') {
      return createFormGroup(key, value);
    }
  }
}

While you could create each element for you target output by means of Document.createElement(), using <template> makes the code more readable.

Every form inputs needs a correctly associated label. Wrapping the input in the label is the easiest way, as otherwise you would need to create a unique ID for each input to be able to address it in the for attribute.

const obj = {
  "name": "Ram",
  "age": 27,
  "vehicles": {
    "car": "limousine",
    "bike": "ktm-duke",
    "airlines": {
      "lufthansa": "Air123",
      "British airways": "Brt707"
    }
  }
};


const formControl = document.querySelector('#formControl');
const formGroup = document.querySelector('#formGroup');
const form = document.querySelector('#form');

const createFormGroup = (key, obj) => {
  const group = formGroup.content.cloneNode(true);

  group.querySelector('legend').textContent = key.charAt(0).toUpperCase() + key.slice(1);

  for (const [key, value] of Object.entries(obj)) {
    let control;
    
    if (typeof value === 'object') {
      // this is the recursive call
      control = createFormGroup(key, value);
    } else {
      control = formControl.content.cloneNode(true);
      control.querySelector('label').prepend(key.charAt(0).toUpperCase() + key.slice(1));

      const i = control.querySelector('input');
      i.name = key;
      i.value = value;
    }

group.querySelector('fieldset').appendChild(control);
  }

  return group;
}

if ('content' in document.createElement('template')) {
  form.appendChild(createFormGroup('Edit Object', obj));
} else {
  console.log('Error, browser version does not support templates');
}
<template id="formControl">
  <p>
    <label>
      <input type="text" value="">
    </label>
  </p>
</template>

<template id="formGroup">
  <fieldset>
    <legend></legend>
  </fieldset>
</template>

<form id="form">
</form>

Values that are not simple strings or numbers

If you take a look at the JavaScript data types and data structures, you will realise that there is a lot of variation possible in an object.

So you will need a lot of switch cases to create input fields that make sense for the data type, if you want to cover any object. If it’s boolean, you might want to generate a checkbox, for example.

If your object is created from JSON (JavaScript Object Notation), the possibilities are limited to

  • string
  • number
  • object
  • array
  • true
  • false
  • null

For the form to work, you will need to have a mechanism to write the values back, which will need to implement conversions as well, to not create strings everywhere. In your current case, if you simply wrote the input values back, you’d get:

{
      "name":"Max",
      "age":"38",
…

If a property is null, it’s difficult to decide what input type to create.

In JavaScript, objects also can contain functions, so you’ll need to decide what to do with those as well.

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement