One to many relationship form

Hello guys, I want to build a form that will make me edit information about Post. Post has title, description and short bullet points, that are associated with the post through one to many relationship. Weird thing is that it works when I want to edit a post that has bullet points, but when creating new one or editing post without bullets, it does not work.
This is my form component:

 @impl true
  def render(assigns) do
    ~H"""
    <div>
      <.header>
        <%= @title %>
        <:subtitle>Użyj tego formularza aby dodać nowego posta</:subtitle>
      </.header>

      <.simple_form
        for={@form}
        id="post-form"
        phx-target={@myself}
        phx-change="validate"
        phx-submit="save"
      >

        <.input field={@form[:title]} type="text" label="Tytuł" />
        <.input field={@form[:description]} type="textarea" label="Opis (do 1000 znaków)" />

        <fieldset>
        <.inputs_for :let={bullet_form} field={@form[:bullet_points]}>
          <.input type="text" field={bullet_form[:content]} label="Bullet" />
        </.inputs_for>
        </fieldset>

        <:actions>
          <.button phx-disable-with="Saving...">Add Post</.button>
        </:actions>
      </.simple_form>
    </div>
    """
  end

  @impl true
  def update(%{post: post} = assigns, socket) do
    changeset = Articles.change_post(post)

    {:ok,
     socket
     |> assign(assigns)
     |> assign_form(changeset)}
  end

  @impl true
  def handle_event("validate", %{"post" => post_params}, socket) do
    changeset =
      socket.assigns.post
      |> Articles.change_post(post_params)
      |> Map.put(:action, :validate)

    {:noreply, assign_form(socket, changeset)}
  end

  def handle_event("save", %{"post" => post_params}, socket) do
    save_question(socket, socket.assigns.action, post_params)
  end

  defp save_question(socket, :edit, post_params) do
    case Articles.update_post(socket.assigns.post, post_params) do
      {:ok, post} ->
        notify_parent({:saved, post})

        {:noreply,
         socket
         |> put_flash(:info, "Post updated successfully")
         |> push_patch(to: socket.assigns.patch)}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply, assign_form(socket, changeset)}
    end
  end

  defp save_question(socket, :new, post_params) do
    case Articles.create_post(post_params) do
      {:ok, post} ->
        notify_parent({:saved, post})

        {:noreply,
         socket
         |> put_flash(:info, "Post created successfully")
         |> push_patch(to: socket.assigns.patch)}

      {:error, %Ecto.Changeset{} = changeset} ->
        {:noreply, assign_form(socket, changeset)}
    end
  end

  defp assign_form(socket, %Ecto.Changeset{} = changeset) do
    assign(socket, :form, to_form(changeset))
  end

  defp notify_parent(msg), do: send(self(), {__MODULE__, msg})

How is it going with this issue now?

I’m trying to do something similar. This rendering pattern is already quite helpful to me to move forward.

@t0nghe have you figured out how to proceed with issue.

Hey :wave:

Have you tried using the Ecto options drop_param and sort_param (doc) for that purpose ?
Here is a nice article about them: Sorting and Deleting many-to-many assocs with Ecto and LiveView · The Phoenix Files
This demo from Chris McCord might also help: https://youtu.be/FADQAnq0RpA?si=aNjxadeLXE3jd-mp&t=2525

1 Like

Sorry for my late reply! I was working on a personal project at the time. I’ve since forgotten about that project (and the problems I met along the way…) (Facepalm)