Is it possible to insert somehow live_component in inputs_for?

Hi, is it possible to do something like this?? :slight_smile:

<%= inputs_for f, :assessment_questions, fn aq -> %>
  .... some inputs
    <div id="root-questions">
      <%= for aq <- @assessment_form.assessment_questions do %>
        <div id={"wrapper-answer-#{aq.id}"}>
             <.live_component module={MyAppWeb.AssessmentFormLive.AnswerComponent} id={"answer-#{aq.id}"} />
        </div>
      <% end %>
    </div>
  <% end %>

elixir 1.13.4-otp-25
phoenix, “~> 1.6.9”
phoenix_live_view, “~> 0.17.8”


I am trying to create a form to create an assessment that contains N questions and each question should have M answers with scores. So I need a nested form for each question in the assessment and a nested form for each answer and score, which will then create options in the question.


Alternatively, does anyone know of a tutorial on inserting a nested form into a nested form or a tutorial that addresses similar functionality I’m trying to achieve please? All I found was nested form with only one level.


I will eventually write my code below this question.

Thank you for any advice and help! :slight_smile:

My Code


form_live.html.heex

<%= form_for @changeset, @action, [multipart: true, phx_change: :validate, csrf_token: @csrf_token], fn f -> %>
  <div>
    <%= label(f, :name, gettext("Assessment Name")) %>
    <%= text_input(f, :name, class: "form-control") %>
    <%= error_tag(f, :name) %>
  </div>

  .... some other inputs

    <%= inputs_for f, :assessment_questions, fn aq -> %>
      <div>
        <div>
          <%= label aq, :question %>
          <%= text_input aq, :question %>
          <%= error_tag aq, :question %>
        </div>

         .... some other inputs

         <div id="root-questions">
           <%= for aq <- @assessment_form.assessment_questions do %>
             <div id={"wrapper-answer-#{aq.id}"}>
                <.live_component module={MyAppWeb.AssessmentFormLive.AnswerComponent} id={"answer-#{aq.id}"} />
             </div>
           <% end %>
        </div>
      <% end %>

    <a href="#" phx-click="add-assessment_question"> Add an assessment question</a>

  <div>
    <%= submit("Save") %>
  </div>
<% end %>

render(assigns) in answer_component.ex

  def render(assigns) do
    ~H"""
        <%= form_for @changeset, @action, [multipart: true, phx_change: :validate_answer, csrf_token: @csrf_token], fn ff -> %>
          <%= inputs_for ff, :assessment_questions, fn aw -> %>
              <div>
                <div>
                  <%= label aw, :title, gettext("Title") %>
                  <%= text_input aw, :title%>
                  <%= error_tag aw, :title %>
                </div>
                
                .... some other inputs

                </div>               
            <% end %>
          <a href="#"> Add an assessment answer</a>
        <% end %>
    """
  end

STRUCTURE

schema in MyApp.Assessments.AssessmentForm

  schema "assessment_forms" do
    field :assessment_deadline, :naive_datetime
    field :description, :string
    field :name, :string
    field :published_at, :naive_datetime

    has_many :assessment_questions,MyApp.Assessments.AssessmentQuestion

    timestamps()
  end

schema in MyApp.Assessments.AssessmentQuestion

  schema "assessment_questions" do
    field :category, :string
    field :description, :string
    field :ordering, :integer
    field :question, :string
    field :temp_id, :string, virtual: true
    field :delete, :boolean, virtual: true

    embeds_many :assessment_answers, AssessmentAnswer, on_replace: :delete

    belongs_to :assessment_form, MyApp.Assessments.AssessmentForm

    timestamps()
  end

embedded_schema in MyApp.Assessments.AssessmentAnswer

  embedded_schema do
    field :title, :string
    field :score, :integer
    field :answ_id, :string, virtual: true
    field(:delete, :boolean, virtual: true)
  end

If you are interested in other parts, I will list them below.

Hi @John_Shelby !

Phoenix components and live components don’t work well when rendered inside anonymous functions, because of how change tracking works. You’re rendering your form and the inputs for your nested forms inside anonymous functions:

<%= form_for @changeset, @action, [multipart: true, phx_change: :validate, csrf_token: @csrf_token], fn f -> %>
...
 <%= inputs_for f, :assessment_questions, fn aq -> %>
...

Instead, I suggest you use tho form component to render the form and the variant of inputs_for that doesn’t use an anonymous function for the nested forms:

<.form :let={f} for={@changeset} action={@action} multipart csrf_token={@csrf_token}  phx-change="validate" >
...
 <%= for aq <- inputs_for f, :assessment_questions do %>
...

<% end %>
</.form>

Hope this heps!

1 Like

You don’t want to be defining a form inside another form (that’s invalid html) like you are in your answer component. Just the inputs/inputs_for is what you use for the embeds.