When using a form that is embedded in a table like so:
def render(assigns) do
~L"""
<h2>Listing Things</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Number</th>
<th></th>
</tr>
</thead>
<tbody>
<%= for thing <- @things do %>
<tr>
<td><%= thing.name %></td>
<td><%= thing.number %></td>
<td>
<button phx-click="delete_thing" phx-value="<%= thing.id %>">Delete</button>
</td>
</tr>
<% end %>
<tr>
<%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save_new_thing], fn f -> %>
<td>
<%= text_input f, :name %>
<%= error_tag f, :name %>
</td>
<td>
<%= text_input f, :number %>
<%= error_tag f, :number %>
</td>
<td><%= submit "Save", phx_disable_with: "Saving..." %></td>
<% end %>
</tr>
</tbody>
</table>
<button phx-click="new_empty_thing">New Thing</button>
"""
end
I'd expect the "validate" event to fire such that it can be handled via
def handle_event("validate", %{"thing" => params}, socket) do
changeset =
%Thing{}
|> Thing.changeset(params)
|> Map.put(:action, :insert)
{:noreply, assign(socket, changeset: changeset)}
end
or be hit by my handler for unhandled events:
def handle_event(unhandled_event, payload, socket) do
IO.inspect unhandled_event, label: "unhandled_event"
{:noreply, socket}
end
No event is received by LiveView handle_event/3 function.
I tried changing the render/1 function such that the form was not inside the table like so:
def render(assigns) do
~L"""
<h2>Listing Things</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Number</th>
<th></th>
</tr>
</thead>
<tbody>
<%= for thing <- @things do %>
<tr>
<td><%= thing.name %></td>
<td><%= thing.number %></td>
<td>
<button phx-click="delete_thing" phx-value="<%= thing.id %>">Delete</button>
</td>
</tr>
<% end %>
<tr>
</tr>
</tbody>
</table>
<button phx-click="new_empty_thing">New Thing</button>
<%= form_for @changeset, "#", [phx_change: :validate, phx_submit: :save_new_thing], fn f -> %>
<%= label f, :name %>
<%= text_input f, :name %>
<%= error_tag f, :name %>
<%= label f, :number %>
<%= text_input f, :number %>
<%= error_tag f, :number %>
<%= submit "Save", phx_disable_with: "Saving..." %>
<% end %>
"""
end
This results in the validation event properly firing and being handled as I'd expect.
The phx-submit event does properly fire when the form is inside of the table, resulting in a Thing record being created.
When the phx-submit event fires but there is a validation error, the changeset properly updates and the error validations appear when the form is in the table.
The handler for save looks like so:
def handle_event("save_new_thing", %{"thing" => params}, socket) do
case Validation.create_thing(params) do
{:ok, thing} ->
notify_subscribers(:save)
all_things = fetch_all_things()
changeset = Validation.change_thing()
{:noreply, assign(socket, things: all_things, changeset: changeset)}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, changeset: changeset)}
end
end
Hi @mbramson, can you please push this to a sample app on GitHub? It will make things much easier for us to reproduce and fix the error. Thank you.
This appears to be an issue with invalid HTML. The browser is rewriting the HTML for the cells to be outside the form, and thus the inputs as well:

In this case, you need to generate the form inside the table cell or avoid the table. I don't believe there's anything we can do in this case.
Most helpful comment
This appears to be an issue with invalid HTML. The browser is rewriting the HTML for the cells to be outside the form, and thus the inputs as well:
In this case, you need to generate the form inside the table cell or avoid the table. I don't believe there's anything we can do in this case.