Created
May 26, 2023 13:36
-
-
Save C-Sinclair/a95bfd76bba9e174485268f8362877db to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule LiveViewDynamicFormIssue do | |
@moduledoc """ | |
Reproduction of a LiveView issue with dynamic nested forms | |
Try adding 2 items to the list, you aren't able to remove the first one | |
Try adding a 3rd item, you can remove the first one now. But then once again you can't remove the first one! | |
""" | |
use Phoenix.Component | |
def mount(_, _, socket) do | |
socket = | |
socket | |
|> assign(:task_id, nil) | |
|> assign(:task, nil) | |
|> assign(:task_changeset, nil) | |
|> assign(:task_form, nil) | |
{:ok, socket} | |
end | |
def handle_params( | |
%{ | |
"task_id" => task_id | |
}, | |
_, | |
socket | |
) do | |
task = %ProjectTask{id: task_id} | |
task_changeset = ProjectTask.changeset(task) | |
form = to_form(task_changeset) | |
socket = | |
socket | |
|> assign(:task, task) | |
|> assign(:task_id, task_id) | |
|> assign(:task_changeset, task_changeset) | |
|> assign(:task_form, form) | |
{:noreply, socket} | |
end | |
def handle_event("change_questions", params, socket) do | |
IO.inspect(params, label: "Params") | |
changeset = | |
socket.assigns.task | |
|> ProjectTask.changeset(params["project_task"] || %{}) | |
|> ProjectTask.update_changeset_questions() | |
{:ok, task} = | |
Ecto.Changeset.apply_action(changeset, :update) | |
|> IO.inspect(label: "Task changeset with questions applied") | |
task_form = | |
changeset | |
|> to_form() | |
# HERE, questions state is not updated when new question is added | |
# |> IO.inspect(label: "Task changeset to form") | |
socket = | |
socket | |
|> assign(:task, task) | |
|> assign(:task_form, task_form) | |
{:noreply, socket} | |
end | |
def render(assigns) do | |
~H""" | |
<.form for={@task_form} phx-change="change_questions" class="flex flex-col"> | |
<.inputs_for :let={question_form} field={@task_form[:questions]}> | |
<.question_form question_form={question_form} /> | |
</.inputs_for> | |
<.add_question_btn /> | |
</.form> | |
""" | |
end | |
def add_question_btn(assigns) do | |
~H""" | |
<label class="cursor-pointer mt-4 self-end"> | |
<input type="checkbox" name="project_task[questions_sort][]" class="!hidden" /> | |
<span class="text-sm text-indigo-blue">+ Add another question</span> | |
</label> | |
""" | |
end | |
def question_form(assigns) do | |
~H""" | |
<div class="flex flex-col relative border-bottom"> | |
<label class="cursor-pointer absolute top-0 right-0"> | |
<i class="fa-solid fa-trash" /> | |
<input | |
type="checkbox" | |
name="project_task[questions_drop][]" | |
value={@question_form.index} | |
class="!hidden" | |
/> | |
</label> | |
<h5 class="mt-0">Question <%= @question_form.index + 1 %></h5> | |
<input type="hidden" name="project_task[questions_sort][]" value={@question_form.index} /> | |
<%= Inputs.labelled_text_area(@question_form, :instruction_body, :small, | |
label: "Write your question instructions or steps" | |
) %> | |
<%= Inputs.labelled_text_area(@question_form, :question_body, :small, | |
label: "Write your question" | |
) %> | |
</div> | |
""" | |
end | |
end | |
defmodule ProjectTask do | |
use Ecto.Schema | |
alias Ecto.Changeset | |
schema "project_tasks" do | |
field(:description, :string) | |
has_many(:questions, ProjectTaskQuestion, | |
on_replace: :delete, | |
on_delete: :delete_all | |
) | |
timestamps(type: :utc_datetime_usec) | |
end | |
@fields [:description] | |
def changeset(project_task, params \\ %{}) do | |
project_task | |
|> Changeset.cast(params, @fields) | |
end | |
def update_changeset_questions(changeset) do | |
changeset | |
|> Changeset.cast_assoc(:questions, | |
with: &ProjectTaskQuestion.changeset/2, | |
sort_param: :questions_sort, | |
drop_param: :questions_drop | |
) | |
end | |
end | |
defmodule ProjectTaskQuestion do | |
use Ecto.Schema | |
alias Ecto.Changeset | |
schema "project_task_questions" do | |
field(:question_body, :string) | |
belongs_to(:project_task, ProjectTask) | |
timestamps(type: :utc_datetime_usec) | |
end | |
@fields [:question_body] | |
def changeset(project_task_question, params \\ %{}) do | |
project_task_question | |
|> Changeset.cast(params, @fields) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment