Hi everyone,
I’m trying to ramp on Phoenix/LiveView by building a simple ToDo app (of course).
I have a table of Todos and an extra row with a form to add a new one. For some reason, when I:
- click the submit button while the form is empty
- successfully add a new Todo and try to add a second
- sometimes delete a Todo
the submit button doesn’t seem to work after. The form won’t submit, the terminal console sees no logs, etc.
Do you know what I may be doing wrong here? To my knowledge, the live-view is clearly refreshing the list of todos; do I need to force a refresh of the form too somehow?
Also, the third cause I noted above is spurious. Sometimes it causes the form to be unusable, sometimes it doesn’t.
Here is the code:
index.html.heex
<table>
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th></th>
</tr>
</thead>
<tbody id="todos">
<%= for todo_id <- @todo_ids do %>
<.live_component module={PromisedWeb.PromiseLive.ShowComponent}
id={todo_id} />
<% end %>
<tr>
<form
phx-submit="create">
<td>
<input
type="text"
name="title"
placeholder="Add a Todo..."
spellcheck="false"
autocomplete="off"
/>
</td>
<td>
<input
type="text"
name="description"
spellcheck="false"
autocomplete="off"
/>
</td>
<td>
<%= submit "Add", phx_disable_with: "Adding..." %>
</td>
</form>
</tr>
</tbody>
</table>
index.ex
defmodule PromisedWeb.PromiseLive.Index do
use PromisedWeb, :live_view
alias Promised.Todos
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket, :todo_ids, list_todo_ids())}
end
@impl true
def handle_event("create", todo, socket) do
IO.inspect("handling event create")
case Todos.create_todo(todo) do
{:ok, _todo} ->
{
:noreply,
socket
# |> put_flash(:info, "Todo created successfully")
|> assign(:todo_ids, list_todo_ids())
}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, :changeset, changeset)}
end
end
@impl true
def handle_event("delete", %{"id" => id}, socket) do
todo = Todos.get_todo!(id)
case Todos.delete_todo(todo) do
{:ok, _} ->
{:noreply,
socket
|> assign(%{todo_ids: list_todo_ids()})}
_ ->
{:noreply, socket}
end
end
@impl true
def handle_info({:updated_todo, %{todo_id: _todo_id}}, socket) do
{:noreply,
socket
|> put_flash(:info, "Todo updated successfully")
|> assign(%{todo_ids: list_todo_ids()})}
end
defp list_todo_ids do
Todos.list_todo_ids()
end
end