sindresorhus / project-ideas

Need a JavaScript module or looking for ideas? Welcome ✨
543 stars 9 forks source link

Deserialize FormData #137

Open fregante opened 1 year ago

fregante commented 1 year ago

There's an excellent module for serializing and deserializing forms: https://www.npmjs.com/package/dom-form-serializer

However this is quite old and we have since gained FormData as a way to serialize forms into a string, which dom-form-serializer doesn't support (it has its own object format)

What's missing is a way to apply the contents of FormData back to a form (rehydrate, if you will).

This form.elements.namedItem() API can help match the field name in FormData to the actual fields, but then the logic to select the right checkbox, radio, select, multi-select isn't straightforward. It'd be great to have a modern, simple way to do this.

Richienb commented 1 year ago

Setting .value seems to work on every element (including checkboxes, radio fields, and selects) except those where there are security concerns. For example, file inputs don't have a .value and trying to use its own API to shim it doesn't work because it seems that the native element doesn't react at all.

Thus:

function hydrateForm(formData, formElement) {
    for (const [name, value] of formData.entries()) {
        const escapedName = CSS.escape(name);
        const input = formElement.querySelector(`[name='${escapedName}']`);

        if (input === null) {
            throw new Error(name === escapedName ? `Form element for ${name} not found` : `Form element for ${name} (escaped: ${escapedName}) not found`);
        }

        input.value = value;
    }
}
fregante commented 1 year ago

seems to work

I don’t think so. "Setting the value" of a checkbox means:

If you just set checkbox.value = 'on' you're overriding the value without actually checking it.

fregante commented 1 year ago

https://jsfiddle.net/2d10cnfg/

Richienb commented 1 year ago

Ok that reveals a slight confict: both of these are saved as the same thing;

<input type="checkbox" name="state" checked>
<input type="checkbox" name="state" value="on" checked>
<input type="checkbox" name="state" value="off">

But that seems pretty minor, though worth documenting if it ever gets published.

What's the use case, btw?

fregante commented 1 year ago

No, that’s the standard behavior of FormData. Unchecked fields are not included in the data. So "unchecked === might as well not exist."

The only thing your example shows is that it will need to match the data state=on to to value-less input fields.

FormData doesn’t save the exact HTML, but just the data. This is the main difference with “classic” serializers which would have a hard time picking the right value for those fields (because they usually save true as the value of a checked checkbox)

fregante commented 1 year ago

What's the use case, btw?

I use the mentioned module in https://github.com/fregante/webext-options-sync