I have a phoenix app consisting of Posts & comments. A post can have many comments. I’m trying to put together a nested input form but failing with one of the handle events. Here is my code:
defmodule LiveBlogWeb.BlogLive.New do
alias LiveBlog.Blog.Comment
use LiveBlogWeb, :live_view
alias LiveBlog.Blog
alias LiveBlog.Blog.Post
alias LiveBlog.Blog.Comment
@impl true
def mount(_params, _session, socket) do
changeset = Blog.change_post(%Post{comments: [%Comment{}]})
# changeset = Blog.change_post(%Post{comments: [%Comment{}]})
form =
%Post{}
|> Post.changeset(%{})
|> to_form(as: "post")
{:ok, socket |> assign(form: form, changeset: changeset)}
end
@impl true
def handle_event("validate", %{"post" => post_params}, socket) do
post = Ecto.Changeset.apply_changes(socket.assigns.changeset)
changeset =
Blog.change_post(post, post_params)
|> Map.put(:action, :validate)
{:noreply, assign(socket, changeset: changeset)}
end
def handle_event("save", %{"post" => post_params}, socket) do
case Blog.create_post(post_params) do
{:ok, _post} ->
{:noreply,
socket
|> put_flash(:info, "Post created successfully.")
|> push_navigate(to: "/blog")}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, changeset: changeset)}
end
end
def handle_event("remove-comment", %{"index" => index}, socket) do
index = String.to_integer(index)
params = socket.assigns.changeset.params || %{}
post = Ecto.Changeset.apply_changes(socket.assigns.changeset)
# data = socket.assigns.changeset.data
changeset =
post
|> Blog.change_post(params)
# comments = get_comments_from_changeset(changeset)
comments = Ecto.Changeset.get_field(changeset, :comments, [])
updated_comments = List.delete_at(comments, index)
updated_changeset =
post
|> Blog.change_post(params)
|> Ecto.Changeset.put_assoc(:comments, updated_comments)
|> Map.put(:action, :validate)
{:noreply, assign(socket, changeset: updated_changeset)}
end
def handle_event("add-comment", _params, socket) do
# Handle adding a comment if needed
params = socket.assigns.changeset.params || %{}
# data = socket.assigns.changeset.data
post = Ecto.Changeset.apply_changes(socket.assigns.changeset)
changeset = Blog.change_post(post, params)
# changeset =
# socket.assigns.changeset.data
# |> Blog.change_post(params)
current_comments = Ecto.Changeset.get_assoc(changeset, :comments)
updated_comments = current_comments ++ [%Comment{}]
IO.inspect(updated_comments)
updated_changeset =
post
|> Blog.change_post(params)
|> Ecto.Changeset.put_assoc(:comments, updated_comments)
|> Map.put(:action, :validate)
# changeset =
# %Post{}
# |> Post.changeset(%{})
# |> Ecto.Changeset.put_assoc(:comments, updated)
{:noreply, assign(socket, changeset: updated_changeset)}
end
defp get_comments_from_changeset(changeset) do
case Ecto.Changeset.get_field(changeset, :comments, []) do
nil -> [%Comment{}]
comments -> comments
end
end
@impl true
def render(assigns) do
~H"""
<div class="max-w-4xl mx-auto py-8 px-4">
<h1>New Post</h1>
<.form :let={f} for={@changeset} id="post-form" phx-submit="save">
<.input field={f[:title]} type="text" label="Title" required />
<.input field={f[:summary]} type="text" label="Summary" required />
<.input field={f[:body]} type="textarea" label="Body" required />
<div id="comments">
<.inputs_for :let={comment} field={f[:comments]}>
<.input
field={comment[:body]}
type="textarea"
label="Comment"
placeholder="Start a comment"
/>
<.button type="button" phx-click="remove-comment" phx-value-index={comment.index}>
Remove
</.button>
</.inputs_for>
</div>
<.button phx-click="add-comment" type="button">
Add Comment
</.button>
<div class="flex justify-end">
<.button phx-disable-with="Saving...">Save</.button>
</div>
</.form>
</div>
"""
end
end
add-comment event adds a new comment input form, but when I try and delete I get the following error:
warning] found duplicate primary keys for association/embed `:comments` in `LiveBlog.Blog.Post`. In case of duplicate IDs, only the last entry with the same ID will be kept. Make sure that all entries in `:comments` have an ID and the IDs are unique between them
Also filling in the post form resets the comments section. Any idea what i’m doing wrong?