So here is my situation, I have a main form on a page, it manages a list of products. Each of these products
is a changeset struct, and it can be added dynamically.
<%= for {product, i} <- @products do %>
<%= live_component @socket, Web.Components.ProductForm, id: i, product: product %>
<% end %>
and in the Web.Components.ProductForm
live component, there are 2 forms, one to actually manage the product information, and another to search and provide an auto-completion of some kind.
<div class="column is-one-quarter">
<%= s = form_for :search, "#", [as: "search-#{@id}", phx_change: :product_search, phx_target: @myself] %>
<%= label s, :query, class: "label" %>
<%= text_input s, :query, class: "input" %>
<datalist>
<%= for {product_id, product_code} <- @search_results do %>
<option value="<%= product_id %>"><%= product_code %></option>
<% end %>
</datalist>
</form>
</div>
<div class="column">
<%= f = form_for @product, "#", [as: "product-#{@id}",phx_change: :form_change, phx_target: @myself] %>
<%= label f, :remarks, class: "label" %>
<%= text_input f, :remarks, class: "input" %>
<%= label f, :quantity, class: "label" %>
<%= number_input f, :quantity, default: 0, class: "input" %>
</form>
</div>
And in the component itself, there are these handlers:
@impl true
def handle_event("form_change", params, socket) do
IO.inspect(params, label: "FORM CHANGE")
{:noreply, socket}
end
@impl true
def handle_event("product_search", params, socket) do
IO.inspect(params, label: "SEARCH CHANGE")
{:noreply, socket}
end
So my questions:
-
When my the main form changes, I got the params value of
%{"product-0" => %{....}
, with theproduct-0
as the map key, which I’m assuming comes from theas
param passed into the form? But when the search form changes, the params has the value of%{"search" => %{....}}
, which seems to come from the atom that are passed into theform_for/4
call? Or is it derived from something else entirely that I’m missing? -
With the above setup, I’m getting an issue where when multiple
@products
are present and multiple components are rendered, only search box for the last product gets rendered. It seems to be having an issue with diffing? The issue went away if I were to useform_for String.to_atom("search-#{@id}")
instead, but I don’t think it is such a good idea to generate atoms dynamically. Is there any other way to generate the forms?