maxmarcon / live_select

Dynamic (multi)selection field for LiveView
https://hex.pm/packages/live_select
Apache License 2.0
185 stars 35 forks source link

Multiple live selects get updated to same value #1

Closed mindok closed 2 years ago

mindok commented 2 years ago

If you have two live selects in the same form, selecting a value for one updates both.

The form is set up as follows:

  <.form for={:unit_converter_form} let={f} phx-change="change" phx-submit="submit" class="w-1/2">
    <div class="flex gap-2 items-center">
      <%= LiveSelect.live_select(f, :unit_from, add_placeholder("Units From", @live_select_opts)) %>
      <%= LiveSelect.live_select(f, :unit_to, add_placeholder("Units To", @live_select_opts)) %>
    </div>
    <%= submit("Submit", class: "btn btn-primary") %>
  </.form>

It appears to be due to the js handleEvents firing for both instances. On my local copy I made a couple of changes to resolve this. I don't know if I am misunderstanding how to use multiple instances, but if these changes make sense, I'm happy to package them into a PR.

I patched the mounted in live_select.js to look like this:

    mounted() {
      this.handleEvent("reset", ({ id: id }) => {
        if (this.el.id == id) { // Note check on matching DOM id
          this.setSearchInputValue("")
          this.setHiddenInputValue("")
        }
      })
      this.handleEvent("selected", ({ selected: [label, selected], id: id }) => {
        if (this.el.id == id) {
          this.setSearchInputValue(label);
          this.setHiddenInputValue(selected)
        }
      })
      this.attachDomEventHandlers()
    },

and the functions that invoke the events in component.ex to look like this:

  defp select(socket, selected_position) do
    {label, selected} = Enum.at(socket.assigns.options, selected_position)
    id = socket.assigns.id # Pass in DOM id

    socket
    |> assign(
      options: [],
      current_focus: -1,
      search_term: label,
      selected: selected
    )
    |> push_event("selected", %{selected: [label, selected], id: id})
  end

  defp reset_input(socket) do
    id = socket.assigns.id

    socket
    |> assign(options: [], selected: nil, search_term: "")
    |> push_event("reset", %{id: id})
  end
maxmarcon commented 2 years ago

@mindok thank you so much! Excellent catch. I thought that https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#push_event/3 would route the event to the right component, but that was a wrong assumption. The docs are pretty clear that this is not the case.

I followed your suggestions and now it's working. Let me know if it works for you too.

closed by: ec34db79754d7423824f435210e75779e618eb82

mindok commented 2 years ago

Looks great thanks!