The form attribute of input
I recently discovered input’s form
attribute in HTML which solved problems I already had in the past.
I sometimes have forms that cannot be easily scoped to a single root in the DOM tree, or that would yield invalid HTML. The last case I had that would produce invalid HTML is a form spanning across table cells: my goal was to make each row in a table “editable”.
Foo | Bar | Baz | Action |
---|---|---|---|
One | Two | Three | Save/Delete buttons |
Four | Five | Six | Save/Delete buttons |
… | … | … | Save/Delete buttons |
Input | Input | Input | Create button |
It is invalid to insert the form element in the middle of table elements:
<table>
<form action="/entity/1/sub-entity" method="POST"> <!-- nope -->
<tr>
<td>...</td>
</tr>
</form>
</table>
One solution would be using javascript, it would have been fine to enhance the table but for something that simple it should not be required, thus I wanted my markup to be sufficient.
A markup-only solution could have been to make a big form containing all inputs with some naming convention and decode all that server side.
But I preferred to keep basic HTTP endpoints that handle proper POST/PUT/DELETE requests for a single row, decoupled from that HTML.
I could also use div
s and grid layout but it means redoing the layout work that browsers perform when we use tables.
<form action="/entity/1/update" method="POST">
<table>
<tr>
<th>Foo</th>
<th></th>
</tr>
<tr>
<td><input name="foo_row_1" /></td>
<td><button type="submit" value="submit_save_row_1">Save</button></td>
<td><button type="submit" value="submit_delete_row_1">Delete</button></td>
</tr>
<tr>
<td><input name="foo_row_new" /></td>
<td><button type="submit" value="submit_create_row">Create</button></td>
</tr>
</table>
</form>
That’s when I discovered input’s form attribute that is meant to allow inputs to live outside of their form tree.
You given an id
to the form, and then give the same value as the form
attribute of every input that is located ouside the form tree.
<form id="my_form">
<input name="some_field" />
<button type="submit">Save</button>
</form>
<input form="my_form" name="other_field" />
The input can be anywhere in the HTML, but its value will be part of the form submit.
My problem became then an HTML-only naming convention for form id and my REST endpoints can stay clean.
<table>
<tr>
<th>Foo</th>
<th></th>
</tr>
<tr>
<td>
<input form="row_1" name="foo" />
</td>
<td>
<form id="row_1" action="/entity/1/sub-entity/1" method="PUT">
<button type="submit">Save</button>
</form>
<form id="row_1" action="/entity/1/sub-entity/1" method="DELETE">
<button type="submit">Delete</button>
</form>
</td>
</tr>
<tr>
<td>
<input form="row_new" name="foo" />
</td>
<td>
<form id="row_new" action="/entity/1/sub-entity" method="POST">
<button type="submit">Create</button>
</form>
</td>
</tr>
</table>
I still have to find a way for the endpoint to properly handle redirects on success and errors. The basic endpoint does not know about my page with a table and redirects to pages of the sub-entity while in my case I want to be back to the table. I want it to redirect to the parent entity (with the table) on success, or to re-display the table with highlighted errors.
But discovering that inputs are no longer tied to the tree of their form will certainly help me again in the future.