NullVoxPopuli/form-data-utils

Utilities for working with the native FormData

TypeScript

27

87 commits

updated Oct 12, 2026

See the code

README

form-data-utils

Demo

A utility function for extracting the FormData as an object from the native <form> element, allowing more ergonomic of usage of The Platform's default form/fields usage.

Each input within your <form> should have a name attribute. (or else the <form> element doesn't know what inputs are relevant)

This will provide values for all types of controls/fields,

  • input: text, checkbox (and checkbox arrays), radio, file, range, etc
  • select
    • behavior is fixed from browser default behavior, where only the most recently selected value comes through in the FormData. This fix only affects <select multiple>
  • submitter (the button/etc that causes the form to submit (if it has a name attribute))

Installation

npm add form-data-utils

Usage

import { dataFrom } from 'form-data-utils';

function handleSubmit(event) {
  event.preventDefault();

  let obj = dataFrom(event);
  //  ^ { firstName: "NVP", isHuman: null, }
}

<template>
  <form onsubmit={{handleSubmit}}>
    <label>
      First Name
      <input type="text" name="firstName" value="NVP" />
    </label>
    <label> 
      Are you a human?
      <input type="checkbox" name="isHuman" value="nah" />
    </label>

    <button type="submit">Submit</button>
  </form>
</template>

Non-primitive values

In some cases, you might want to select values that aren't primitive types, e.g selecting a user from a list of users. Unfortunately, FormData only supports strings. To work around this, form-data-utils provides some utility functions to "attach" a non-primitive value to an element:

  • setValue(element: HTMLElement, value: unknown) - binds a given value to a given element (value can be anything, e.g objects, arrays, etc)
  • deleteValue(element: HTMLElement) - undoes a previous setValue call

After setting a value with setValue, that value will be considered by the dataFrom function.

[!NOTE]
The ideal way to call these functions will probably vary with the framework you're using.

For example, in Ember.js, you would use these functions in a modifier that you can then apply to elements.

import { dataFrom, deleteValue, setValue } from 'form-data-utils';
import { modifier } from 'ember-modifier';

// define the ember specific modifier
const associateValue = modifier((element, [value]) => {
  setValue(element, value);

  return () => deleteValue(element);
});

function handleSubmit(event) {
  event.preventDefault();

  let obj = dataFrom(event);
  //  ^ { admin: { id: 123, name: 'Sam...' }, user: { id: 321, name: 'Chris...' } }
}

<template>
  <form onsubmit={{handleSubmit}}>
    {{#each users as |user|}}
      <div>
        <input type="radio" name="admin" value={{user.id}} {{associateValue user}} />
        <label for={{user.id}}>{{user.name}}</label>
      </div>
    {{/each}}

    <select name="user">
      <option value=""></option>

      {{#each users as |user|}}
        <option value={{user.id}} {{associateValue user}}>{{user.name}}</option>
      {{/each}}
    </select>

    <button type="submit">Submit</button>
  </form>
</template>

setValue (and deleteValue) doesn't really care how you call it. It just needs an element and a value, that's it.

[!NOTE]
setValue only supports <input type="checkbox", <input type="radio" and <select.

[!WARNING]
The value given by setValue has priority. This means that providing both a value attribute and a value via setValue will result in the value attribute being ignored by dataFrom. However, you might still use the value attribute it to create a more meaningful html structure.

Value types normalization

Another thing that form-data-utils does is to normalize the types of the values you get back from dataFrom. The following table lists the types you get back depending on the various controls available:

ControlTypeDefault (empty) value
<input type="text" (email, search, etc)string''
<input type="number"numbernull
<input type="range"numberranges can't be empty
<input type="date"Datenull
<input type="datetime-local"Datenull
<input type="file"Filenull
<input type="file" multipleFile[][]
<input type="checkbox" without valuebooleanfalse
<input type="checkbox" with valueGivenValueTypenull
multiple <input type="checkbox" with same name without valueboolean[][]
multiple <input type="checkbox" with same name with valueGivenValueType[][]
<input type="radio" without valuebooleanfalse
<input type="radio" with valueGivenValueTypenull
<selectGivenValueTypenull
<select multipleGivenValueType[][]
<button type="submit" with nameGivenValueTypenull

[!NOTE]
GivenValueType here means the type of the value you passed in the value attribute or using the setValue(element, value) function.

Contributing

See the Contributing guide for details.

License

This project is licensed under the MIT License.

NullVoxPopuli/form-data-utils

Utilities for working with the native FormData

TypeScript

27

87 commits

updated Oct 12, 2026

See the code

README

form-data-utils

Demo

A utility function for extracting the FormData as an object from the native <form> element, allowing more ergonomic of usage of The Platform's default form/fields usage.

Each input within your <form> should have a name attribute. (or else the <form> element doesn't know what inputs are relevant)

This will provide values for all types of controls/fields,

  • input: text, checkbox (and checkbox arrays), radio, file, range, etc
  • select
    • behavior is fixed from browser default behavior, where only the most recently selected value comes through in the FormData. This fix only affects <select multiple>
  • submitter (the button/etc that causes the form to submit (if it has a name attribute))

Installation

npm add form-data-utils

Usage

import { dataFrom } from 'form-data-utils';

function handleSubmit(event) {
  event.preventDefault();

  let obj = dataFrom(event);
  //  ^ { firstName: "NVP", isHuman: null, }
}

<template>
  <form onsubmit={{handleSubmit}}>
    <label>
      First Name
      <input type="text" name="firstName" value="NVP" />
    </label>
    <label> 
      Are you a human?
      <input type="checkbox" name="isHuman" value="nah" />
    </label>

    <button type="submit">Submit</button>
  </form>
</template>

Non-primitive values

In some cases, you might want to select values that aren't primitive types, e.g selecting a user from a list of users. Unfortunately, FormData only supports strings. To work around this, form-data-utils provides some utility functions to "attach" a non-primitive value to an element:

  • setValue(element: HTMLElement, value: unknown) - binds a given value to a given element (value can be anything, e.g objects, arrays, etc)
  • deleteValue(element: HTMLElement) - undoes a previous setValue call

After setting a value with setValue, that value will be considered by the dataFrom function.

[!NOTE]
The ideal way to call these functions will probably vary with the framework you're using.

For example, in Ember.js, you would use these functions in a modifier that you can then apply to elements.

import { dataFrom, deleteValue, setValue } from 'form-data-utils';
import { modifier } from 'ember-modifier';

// define the ember specific modifier
const associateValue = modifier((element, [value]) => {
  setValue(element, value);

  return () => deleteValue(element);
});

function handleSubmit(event) {
  event.preventDefault();

  let obj = dataFrom(event);
  //  ^ { admin: { id: 123, name: 'Sam...' }, user: { id: 321, name: 'Chris...' } }
}

<template>
  <form onsubmit={{handleSubmit}}>
    {{#each users as |user|}}
      <div>
        <input type="radio" name="admin" value={{user.id}} {{associateValue user}} />
        <label for={{user.id}}>{{user.name}}</label>
      </div>
    {{/each}}

    <select name="user">
      <option value=""></option>

      {{#each users as |user|}}
        <option value={{user.id}} {{associateValue user}}>{{user.name}}</option>
      {{/each}}
    </select>

    <button type="submit">Submit</button>
  </form>
</template>

setValue (and deleteValue) doesn't really care how you call it. It just needs an element and a value, that's it.

[!NOTE]
setValue only supports <input type="checkbox", <input type="radio" and <select.

[!WARNING]
The value given by setValue has priority. This means that providing both a value attribute and a value via setValue will result in the value attribute being ignored by dataFrom. However, you might still use the value attribute it to create a more meaningful html structure.

Value types normalization

Another thing that form-data-utils does is to normalize the types of the values you get back from dataFrom. The following table lists the types you get back depending on the various controls available:

ControlTypeDefault (empty) value
<input type="text" (email, search, etc)string''
<input type="number"numbernull
<input type="range"numberranges can't be empty
<input type="date"Datenull
<input type="datetime-local"Datenull
<input type="file"Filenull
<input type="file" multipleFile[][]
<input type="checkbox" without valuebooleanfalse
<input type="checkbox" with valueGivenValueTypenull
multiple <input type="checkbox" with same name without valueboolean[][]
multiple <input type="checkbox" with same name with valueGivenValueType[][]
<input type="radio" without valuebooleanfalse
<input type="radio" with valueGivenValueTypenull
<selectGivenValueTypenull
<select multipleGivenValueType[][]
<button type="submit" with nameGivenValueTypenull

[!NOTE]
GivenValueType here means the type of the value you passed in the value attribute or using the setValue(element, value) function.

Contributing

See the Contributing guide for details.

License

This project is licensed under the MIT License.

Project health

Latest release

0.6.0 · Jun 8, 2024

Releases this year

0