Hi! I’m wondering how the community has solved the problem as stated in the topic title: how a live component rendered inside a form should handle its own internal form inputs.
Allow me to explain. Let’s say, for example, we have a LiveView that contains a form, with various input types. In addition to the form elements, we also render a live component inside that form. For example maybe a “searchable dropdown” or a “autocomplete” search input component or something like that. Here’s a simple example:
Parent LiveView with a form:
defmodule MyAppWeb.TestLive do
use MyAppWeb, :live_view
alias MyAppWeb.NestedTestComponent
def mount(_params, _session, socket) do
{:ok, socket}
end
def render(assigns) do
~H"""
<div>
<h1>My LiveView</h1>
<.form let={f} for={:test} phx-change="test">
<%= label f, :parent_liveview_input %>
<%= text_input f, :parent_liveview_input, class: "border-2" %>
<.live_component
id={:nested}
module={NestedTestComponent}
/>
</.form>
</div>
"""
end
end
Nested LiveComponent rendered inside the form:
defmodule MyAppWeb.NestedTestComponent do
use MyAppWeb, :live_component
def mount(_params, _session, socket) do
{:ok, socket}
end
def handle_event("nested_test", _, socket) do
IO.inspect("this event never runs")
{:noreply, socket}
end
def render(assigns) do
~H"""
<div>
<h1>My Nested LiveView Component</h1>
<form id="wow"></form>
<form phx-change="nested_test" phx-target={@myself}>
<input type="text" name="nested_component_input" class="border-2" />
</form>
</div>
"""
end
end
This seems like it would work. The problem arises, however, in the nature of html and most browsers disallowing nested <form />
elements. The nested element is removed completely from the DOM. I know this at least happens in Chrome and Firefox.
My personal use case for such a pattern is an “autocomplete” component, where I would pass in the Phoenix form
into my nested component, which would then render a hidden input that would be registered with the passed in form
coming from the parent liveview. I would have an internal text input bound to a phx-change
event inside my component where the user could “search” for a certain item in a collection, and on select, I would update the hidden input with that selection.
This way, state and business concerns are encapsulated in the live component, and the parent liveview does not need to “know” about how an item in my nested live component is selected, only that it will be selected, and will apply to the changeset given to the parent form.
In summary, I would love to hear how the community has solved the problem of a nested live component rendered inside a form handles its own phx-change
etc… events, given that the nested live component cannot itself contain a form, due to browser/html restrictions. Thank you in advance.