I was following a simple todo list tutorial and ran into an issue when trying to convert some logic into a LiveComponent. I am trying to generate a list of Todo items (which are LiveComponents), but I can’t figure out why newly created Todo items do not render a checkbox next to them. They will only appear until after I refresh the page. Any guidance would be greatly appreciated!
The LiveView todo_live.ex
:
defmodule TodoListWeb.TodoLive do
use TodoListWeb, :live_view
alias TodoList.Todos
def mount(_params, _session, socket) do
Todos.subscribe()
{:ok, fetch(socket)}
end
def handle_event("add", %{"todo" => todo}, socket) do
Todos.create_todo(todo)
{:noreply, fetch(socket)}
end
def handle_event("delete", %{"id" => id}, socket) do
todo = Todos.get_todo!(id)
Todos.delete_todo(todo)
{:noreply, fetch(socket)}
end
def handle_info({Todos, [:todo | _], _}, socket) do
{:noreply, fetch(socket)}
end
def handle_info({:update, %{"id" => id, "title" => title}}, socket) do
todo = Todos.get_todo!(id)
Todos.update_todo(todo, %{title: title})
{:noreply, fetch(socket)}
end
def handle_info({:toggle_completed, %{"id" => id}}, socket) do
todo = Todos.get_todo!(id)
Todos.update_todo(todo, %{completed: !todo.completed})
{:noreply, fetch(socket)}
end
defp fetch(socket) do
assign(socket, todos: Todos.list_todos())
end
end
todo_live.html.leex
:
<div>
<h1>Todo List</h1>
<form action="#" phx-submit="add">
<%= text_input :todo, :title, placeholder: "What do you want to do?" %>
<%= submit "Add", phx_disable_with: "Adding..." %>
</form>
<%= for todo <- @todos do %>
<%= live_component @socket, TodoListWeb.TodoComponent, id: todo.id, todo: todo %>
<% end %>
</div>
The LiveComponent todo_component.ex
:
defmodule TodoListWeb.TodoComponent do
use TodoListWeb, :live_view
use Phoenix.LiveComponent
alias TodoList.Todos
def mount(socket) do
{:ok, assign(socket, updating?: false)}
end
def handle_event("pre_update", _, socket) do
{:noreply, assign(socket, updating?: true)}
end
def handle_event("cancel", _, socket) do
{:noreply, assign(socket, updating?: false)}
end
def handle_event("update", %{"todo" => %{"title" => title}}, socket) do
send self(), {:update, %{"id" => socket.assigns.id, "title" => title}}
{:noreply, assign(socket, updating?: false)}
end
def handle_event("toggle_completed", _, socket) do
send self(), {:toggle_completed, %{"id" => socket.assigns.id}}
{:noreply, socket}
end
end
todo_component.html.leex
:
<div>
<%= checkbox(:todo, :completed, phx_click: "toggle_completed", value: @todo.completed, phx_target: @myself) %>
<%= @todo.title %>
<%= if @updating? do %>
<form action="#" phx-submit="update" phx-target="<%= @myself %>">
<%= text_input :todo, :title, value: @todo.title %>
<%= submit "Save", phx_disable_with: "Saving..." %>
<button type="button" phx-click="cancel" phx-target="<%= @myself %>">Cancel</button>
</form>
<% else %>
<button
phx-click="pre_update"
phx-target="<%= @myself %>"
>
Update
</button>
<% end %>
<button
class="todo-delete-button"
phx-click="delete"
phx-value-id="<%= @todo.id %>"
>
Delete
</button>
</div>