How do I create a select-element in a liveview-form?

Hi, I’m trying to learn Elixir and Phoenix LiveView, now I’m stuck on how to create a dropdown/select in the automatically generated form_component.ex. There is no <.select>, and I have tried using <select> but it’s not working very good.

This is my setup:

mix phx.new testing
cd testing
mix phx.gen.live Categories Category categories name:string
mix phx.gen.live Products Product products name:string category_id:references:categories

Then adding the suggested routes to the router.ex-file. I have also modified my schemas:

defmodule Testing.Categories.Category do
  use Ecto.Schema
  import Ecto.Changeset

  schema "categories" do
    field :name, :string
    has_many :products, Testing.Products.Product

    timestamps(type: :utc_datetime)
  end

  @doc false
  def changeset(category, attrs) do
    category
    |> cast(attrs, [:name])
    |> validate_required([:name])
  end
end

and

defmodule Testing.Products.Product do
  use Ecto.Schema
  import Ecto.Changeset

  schema "products" do
    field :name, :string
    belongs_to :category, Testing.Products.Product

    timestamps(type: :utc_datetime)
  end

  @doc false
  def changeset(product, attrs) do
    product
    |> cast(attrs, [:name, :category_id])
    |> validate_required([:name, :category_id])
  end
end

Finally I have run mix ecto.create and mix ecto.migrate.
The form_component.ex generated for my products has the following form:

      <.simple_form
        for={@form}
        id="product-form"
        phx-target={@myself}
        phx-change="validate"
        phx-submit="save"
      >
        <.input field={@form[:name]} type="text" label="Name" />
        <:actions>
          <.button phx-disable-with="Saving...">Save Product</.button>
        </:actions>
      </.simple_form>

Not sure if I got everything right so far, but I can create a new “Category” with the generated code. When I go to localhost:4000/products and try to create a new product, the form only contains an input for the name. I want to be able to select a (previously created) category from a list, so that I can save my product with a correct category. As I mentioned, I have tried different methods but I can’t get it to work. When I google for sample code, the code that’s been generated for my liveviews doesn’t have much in common with the code samples I find online.

And how comes phx.gen.live doesn’t generate forms with elements for the relations? Even if you don’t always need to select relations from a drop down-list it would have made it easier to understand everything for a beginner, and also when building quick prototypes and admins.

<.input type="select" options={…} />

<.input> is a generic input rendering component and not some 1:1 mapping to just <input>

2 Likes

Thanks that made it clearer. Now I have tried to populate the options from my Testing.Categories.list_categories(), which I guess is some kind of Ecto “result set”. Is there a method to use this directly with the options in the input, or do I have to manually pick out the name and id from it?

Edit:
This worked, but I’m not sure if it’s the proper way of doing it:

<.input field={@form[:category_id]} type="select"
          options={for category <- Testing.Categories.list_categories() do {category.name, category.id} end} />

Yep, that’s how it’s done. Select options are a list of tuples in the form of {text, id}. I usually put it in assigns, though if it’s static it doesn’t much matter I don’t think:

category_options = Enum.map(Testing.Categories.list_categories(), &{&1.name, &1.id})

socket =
  socket
  |> assign(:category_options, category_options)

<.input field={@form[:category_id]} type="select" options={@category_options} />
3 Likes