Ideas on how to nest forms (for filtering fields in forms)?

Hi,

I’ve run into a problem and I don’t know what’s the best way for solving it.

I was creating a selector component for forms, where the user can select many items. For example, when creating a task, a user has to select which users must complete it.

The selector may have many items, so I wanted to add a search box to filter through the list. To make it more reusable, my idea was to pass as a prop a query function that accepts a search option, and when the user types, the component handles the event, and runs the query function with the search option updating the elements in the selector.

def update(%{id: id, query: query}, socket) do
  items = query.(search: "the search query")
  socket = socket
  |> assigns(items: items)
  
  {:ok, socket}
end

The problem is that I would end up with nested forms:

Form > Selector Field > Form (for filtering)

But browsers don’t allow nested forms and so the inner form gets converted into a div, this means that every time I type to filter, the outer form’s phx-change event gets triggered. I could implement a handle event in the outer component, but that would mean that I would have write the filtering logic each time I use the selector component and trigger a send_update to the selector component to update the elements. This seems like a bit of a mess.

As a solution I was thinking of creating a pseudo-form component using Hooks that captures change events in the inputs, stops the bubbling of the event so that the outer form is not validated and triggers a change event. I’m not fully onboard with this solution either.

With the hook it would look like this:

Form > Selector Field > Div (with Filter hook that captures change events)

Any ideas? Should I frame the problem differently? If you have encountered a similar problem, how did you solve it?

Put the forms aside each other and use form="formId" attribute to link inputs and buttons and such to their related forms.

Thanks for the reply @LostKobrakai

What I’m trying to do is this:

# Main live view/component
def render(assigns) do
 ~sh"""
 <.form>
    ... other form inputs
    <.live_component module={Selector} .../>
    ... other form inputs
</.form>
  """
end

# Selector module
defmodule Selector do
   def render(assigns) do
    ~sh"""
    <div>
        <.form> filtering </.form>
       <.input/> <!-- That is affected by filtering -->
    </div>
    """
  end
end

I guess I could do something like this:

# Main live view/component
def render(assigns) do
 ~sh"""
 <.form>
    ... other form inputs
</.form>
<.live_compoment module={Selector} phx-change="same as sibling forms"/>
 <.form>
    ... other form inputs
</.form>
  """
end


# Selector module
defmodule Selector do
   def render(assigns) do
    ~sh"""
    <div>
        <.form> filtering </.form>
       <.form phx-change="using the prop">
          <.input/> <!-- That is affected by filtering -->
       </.form>
    </div>
    """
  end
end

But in this case when one form changes it doesn’t trigger a change on the other forms, so you would need to store the state of the form and apply the changes, so that when you submit it, you have all the info.