petalframework / petal_components

Phoenix + Live View HEEX Components
https://petal.build/components
MIT License
845 stars 96 forks source link

combo box docs enhanced remote_options_event_name example #356

Open debugmenot opened 1 month ago

debugmenot commented 1 month ago

Hi guys! Combo box is an independent great component and it can be purchased separately. So i think there should be some improvements in documentation, at least in most used cases. For example, when we're using remote_options_target for live search (like in live_select hex package), we should keep the combo box selected options tuple on validate and on save, i think for some users its not so intuitive, and "resetting" selected value can be a little bit confusing. So, here is my example, can be helpful for someone :)

heex:

   <.combo_box
      remote_options_event_name="combo_box_product_search"
      remote_options_target={@myself}
      label="Product"
      options={@product_selector_option}
      placeholder="Search for a product..."
      field={@form[:product_id]}
    />

products.ex:

  def search_products_by_name_or_slug(nil), do: []
  def search_products_by_name_or_slug(""), do: []
  def search_products_by_name_or_slug(search_string) when is_binary(search_string) do
    search_pattern = "%#{search_string}%"

    from(p in Product,
      where: ilike(p.name, ^search_pattern) or ilike(p.slug, ^search_pattern),
      select: {p.id, p.name},
      limit: 5
    )
    |> Repo.all()
  end

form_component.ex:

  @impl true
  def handle_event("validate", %{"license" => license_params}, socket) do
    changeset =
      socket.assigns.license
      |> Licenses.change_license(license_params)
      |> Map.put(:action, :validate)

    socket =
      if socket.assigns.product_selector_option == [] && license_params["product_id"] not in [nil, ""] do
        selected_product = PetalPro.Products.get_product!(license_params["product_id"])
        assign(socket, :product_selector_option, [{selected_product.name, selected_product.id}])
      else
        socket
      end

    {:noreply, assign(socket, form: to_form(changeset))}
  end

  def handle_event("save", %{"license" => license_params}, socket) do
    socket =
      if socket.assigns.product_selector_option == [] && license_params["product_id"] not in [nil, ""] do
        selected_product = PetalPro.Products.get_product!(license_params["product_id"])
        assign(socket, :product_selector_option, [{selected_product.name, selected_product.id}])
      else
        socket
      end
    save_license(socket, socket.assigns.action, license_params)
  end

  @impl true
  def handle_event("combo_box_product_search", payload, socket) do
    results =
      PetalPro.Products.search_products_by_name_or_slug(payload)
      |> Enum.map(&%{text: elem(&1, 1), value: elem(&1, 0)})

    {:reply, %{results: results}, socket}
  end
debugmenot commented 1 month ago

Maybe there is more "petalish" way :) but for now its an ready2go alternative to something like ":live_select" (hex)