使用 FormData 从表单到对象数组

Form to Array of Object using FormData

我正在开发一个用户可以添加或删除朋友信息的表单。
我当前的代码如下所示:

(() => {
  let currentCount = 0;
  const theForm = document.querySelector("#theForm");

  document
    .querySelector("#addMoreFields")
    .addEventListener("click", (event) => {
      event.preventDefault();

      // find and clone the template content
      const template = theForm.getElementsByTagName("template")[0];
      const clone = template.content.cloneNode(true);
      const submitButton = document.querySelector("#submitForm");

      // if the submitButton is hidden, show it
      if (submitButton.offsetParent === null) {
        submitButton.removeAttribute("style");
      }

      // add class to set and name on input fields
      clone.querySelector(".set").classList.add(`set-${currentCount}`);
      clone
        .querySelector(".first_name")
        .setAttribute("name", `firstName[${currentCount}]`);
      clone
        .querySelector(".middle_name")
        .setAttribute("name", `middleName[${currentCount}]`);
      clone
        .querySelector(".last_name")
        .setAttribute("name", `lastName[${currentCount}]`);
      clone.querySelectorAll(".checkbox").forEach(function (item) {
        item.setAttribute("name", `checkbox[${currentCount}]`);
      });
      clone.querySelectorAll(".radio").forEach(function (item) {
        item.setAttribute("name", `radio[${currentCount}]`);
      });

      // remove button
      clone
        .querySelector(".removeSet")
        .setAttribute("id", `remove-${currentCount}`);
      clone
        .querySelector(".removeSet")
        .setAttribute("data-number", currentCount);

      theForm.append(clone);

      // add event listener to removeSet button
      document
        .querySelector(`#remove-${currentCount}`)
        .addEventListener("click", (event) => {
          event.preventDefault();
          const setName = `.set-${event.target.getAttribute("data-number")}`;
          document.querySelector(setName).remove();
        });

      currentCount++;
    });

  theForm.addEventListener("submit", (event) => {
    event.preventDefault();

    const theFormData = new FormData(theForm);
    /*
      we need to format the form as:
      ```
      [
        {
          "first_name": "x",
          "middle_name": "x",
          "last_name": "x",
          "checkbox": ['checkbox a', 'checkbox c'],
          "radio": "radio b",
          "textarea": "some text"
        },
        {
          "first_name": "y",
          "middle_name": "y",
          "last_name": "y",
          "checkbox": ['checkbox a', 'checkbox c'],
          "radio": "radio b",
          "textarea": "some text"
        },
        ...
      ]
      ```
    */
  });
})();
.set {
  margin: 5px;
  padding: 5px;
  outline: 1px solid #ccc;
}
.removeSet {
  float: right;
}
<form id="theForm">
  <template>
    <div class="set">
      <button class="removeSet">remove</button>
      <input class="first_name" placeholder="first name" /><br>
      <input class="middle_name" placeholder="middle name" /><br>
      <input class="last_name" placeholder="last name" /><br>
      <!-- checkbox -->
      <label><input type="checkbox" class="checkbox" value="checkbox a" />checkbox a</label>
      <label><input type="checkbox" class="checkbox" value="checkbox b" />checkbox b</label>
      <label><input type="checkbox" class="checkbox" value="checkbox c" />checkbox c</label><br>
      <!-- radio -->
      <label><input type="radio" class="radio" value="radio a" />radio a</label>
      <label><input type="radio" class="radio" value="radio b" />radio b</label>
      <label><input type="radio" class="radio" value="radio c" />radio c</label><br>
      <!-- textarea -->
      <textarea class="textarea" rows="4" cols="50">additional notes</textarea>
    </div>
  </template>
  <button id="addMoreFields">add</button>
  <button id="submitForm" style="display: none;">submit</button>
</form>

我的问题是,如何创建一个对象数组以发送到后端,格式如下:

[
  {
    "first_name": "x",
    "middle_name": "x",
    "last_name": "x",
    "checkbox": ['checkbox a', 'checkbox c'],
    "radio": "radio b",
    "textarea": "some text"
  },
  {
    "first_name": "x",
    "middle_name": "x",
    "last_name": "x",
    "checkbox": ['checkbox a', 'checkbox c'],
    "radio": "radio b",
    "textarea": "some text"
  },
  ...
]

我正在使用 new FormData,但我愿意接受其他选择。
我们只使用 vanilla javascript 所以 jQuery 不是一个选项

这就是我解决问题的方法。
我添加了注释以使其易于理解。 ✌

(() => {
  let currentCount = 0;
  const theForm = document.querySelector("#theForm");

  document
    .querySelector("#addMoreFields")
    .addEventListener("click", (event) => {
      event.preventDefault();

      // find and clone the template content
      const template = theForm.getElementsByTagName("template")[0];
      const clone = template.content.cloneNode(true);
      const submitButton = document.querySelector("#submitForm");

      // if the submitButton is hidden, show it
      if (submitButton.offsetParent === null) {
        submitButton.removeAttribute("style");
      }

      // add class to set and name on input fields
      clone.querySelector(".set").classList.add(`set-${currentCount}`);
      clone
        .querySelector(".first_name")
        .setAttribute("name", `firstName[${currentCount}]`);
      clone
        .querySelector(".middle_name")
        .setAttribute("name", `middleName[${currentCount}]`);
      clone
        .querySelector(".last_name")
        .setAttribute("name", `lastName[${currentCount}]`);
      clone.querySelectorAll(".checkbox").forEach(function (item) {
        item.setAttribute("name", `checkbox[${currentCount}]`);
      });
      clone.querySelectorAll(".radio").forEach(function (item) {
        item.setAttribute("name", `radio[${currentCount}]`);
      });
      clone
        .querySelector(".textarea")
        .setAttribute("name", `textarea[${currentCount}]`);

      // remove button
      clone
        .querySelector(".removeSet")
        .setAttribute("id", `remove-${currentCount}`);
      clone
        .querySelector(".removeSet")
        .setAttribute("data-number", currentCount);

      theForm.append(clone);

      // add event listener to removeSet button
      document
        .querySelector(`#remove-${currentCount}`)
        .addEventListener("click", (event) => {
          event.preventDefault();
          const setName = `.set-${event.target.getAttribute("data-number")}`;
          document.querySelector(setName).remove();
        });

      currentCount++;
    });

  theForm.addEventListener("submit", (event) => {
    event.preventDefault();

    const theArray = [];
    const theFormData = new FormData(theForm);

    // cycle through FormData keys
    for(const [key, value] of theFormData.entries()) {
    
        if (!value) {
          // do validation here
          return;
        }

      // get the key number
      const keyNumber = key.match(/\d/g).join('');
      // assign the object to theArray variable
      theArray[keyNumber] = {
        'first_name': theFormData.get(`firstName[${keyNumber}]`),
        'middle_name': theFormData.get(`middleName[${keyNumber}]`),
        'last_name': theFormData.get(`lastName[${keyNumber}]`),
        'checkbox': theFormData.getAll(`checkbox[${keyNumber}]`),
        'radio': theFormData.get(`radio[${keyNumber}]`),
        'textarea': theFormData.get(`textarea[${keyNumber}]`)
      }      

    }
    
    // clean theArray before sending to the back-end
    const filteredArray = theArray.filter(Boolean);
    console.log(filteredArray);

  });
})();
.set {
  margin: 5px;
  padding: 5px;
  outline: 1px solid #ccc;
}
.removeSet {
  float: right;
}
<form id="theForm">
  <template>
    <div class="set">
      <button class="removeSet">remove</button>
      <input class="first_name" placeholder="first name" /><br>
      <input class="middle_name" placeholder="middle name" /><br>
      <input class="last_name" placeholder="last name" /><br>
      <!-- checkbox -->
      <label><input type="checkbox" class="checkbox" value="checkbox a" />checkbox a</label>
      <label><input type="checkbox" class="checkbox" value="checkbox b" />checkbox b</label>
      <label><input type="checkbox" class="checkbox" value="checkbox c" />checkbox c</label><br>
      <!-- radio -->
      <label><input type="radio" class="radio" value="radio a" />radio a</label>
      <label><input type="radio" class="radio" value="radio b" />radio b</label>
      <label><input type="radio" class="radio" value="radio c" />radio c</label><br>
      <!-- textarea -->
      <textarea class="textarea" rows="4" cols="50">additional notes</textarea>
    </div>
  </template>
  <button id="addMoreFields">add</button>
  <button id="submitForm" style="display: none;">submit</button>
</form>