TIL: How to add a form in a table in Phoenix LiveView?
Suppose, we want to add an inline edit feature on a table row. It can be a little tricky to implement. I had a hard time implementing it because I wasn't about one of the concepts of HTML that we cannot add a form
tag inside a table
. Something like this
<table>
.....
.....
.....
<tbody>
<tr>
<form>
<td>
<input type="text" name="count">
</td>
<td>
<input type="text" name="rate">
</td>
<td>
<input type="text" name="paid">
</td>
</form>
</tr>
</tbody>
</table>
This wasn't working because placing a form as a child element of a table
, tbody
, or tr
is not allowed. If we add it the browser typically relocates the form to appear after the table, leaving its contents, such as table rows, cells, inputs, etc., in their original positions within the table. Hence, when we try to submit the form it won't work.
How can we fix this problem?
To solve this problem we can use the form
attribute. For this, we will place an empty form somewhere within the page and outside of the table
tag. We need to provide the form an id
attribute. This id
will be an identifier for the form fields. Something like this
<.simple_form for={@distribution_record_form} id="distribution_record" phx-submit="save" phx-update="ignore" class="flex">
</.simple_form>
And later inside the table for each field i.e. cell we can add the form
attribute to tell the fields to which form it belongs. It should be the same as the id
of the form
.
<td>
<.input field={@distribution_record_form[:rate]} form="distribution_record" />
</td>
The form
attribute will tell the input
field that it is part of distribution_record
form
. So, when the user tries to submit the form whatever data the user will add in this input box will be part of the distribution_record
form. Our requirement is that each row i.e. tr
should appear as a form in which the last column will have a submit
button. So below is the code of how each row will appear.
<tr>
<td>
<.input field={@distribution_record_form[:count]} form="distribution_record" />
</td>
<td>
<.input field={@distribution_record_form[:rate]} type="text" form="distribution_record" />
</td>
<td><.input field={@distribution_record_form[:paid]} type="text" form="distribution_record" />
</td>
<td>
<.button phx-disable-with="Saving..." form="distribution_record">
Update
</.button>
</td>
</tr>
The above code will map to the form
one that we created earlier. The below diagram will give you an idea of how this mapping is going to work.
I hope this diagram will make sense to you. I know this is quite a trivial thing but I wasn't aware until I came across this problem and I noted it in my TIL list and shared it in this blog. I hope you like this TIL
blog. If you have any questions please comment below. Thanks for reading ๐.
Subscribe to my newsletter
Read articles from AbulAsar S. directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
AbulAsar S.
AbulAsar S.
I am a Software Engineer from Mumbai, India. In love with Functional programming ( precisely Elixir). Love to share the knowledge that I learn while developing things.