hendri-tobing

hendri-tobing

Populate a Select field based on previous Select field

I have database tables: Companies, Devices, and Signs. The device belongs_to a Company and the Company has_many Signs.

I want to show on the new device form:

  • a select box of the existing Companies, and
  • a select box of the Signs that is populated based on the company selection.

The select box of the signs is dynamically changed if the selected company changed.

How to achieve this?

So far this is my device_controller.ex

defmodule PortalWeb.DeviceController do
  use PortalWeb, :controller

  alias Portal.Fleet
  alias Portal.Fleet.Device


  def create(conn, %{"device" => device_params}) do
    with {:ok, %Device{} = device} <- Fleet.create_device_customer(device_params) do
      conn
      |> put_flash(:info, "The device is created successfuly")
      |> redirect(to: Routes.page_path(conn, :index))      
    end
  end

  def new(conn, _params) do
    changeset = Fleet.change_device(%Device{})
    customers = Fleet.list_customers()
    |> Enum.map(&{"#{&1.name}", &1.id})
    render(conn, "new.html", changeset: changeset, customers: customers)
  end
end

Thank you.

First Post!

lucaong

lucaong

You will have to use either JavaScript or LiveView for this. Any preference between these two options?

Most Liked

sfusato

sfusato

Basically you would have a phx-change event on the form which will fire on every input change. Then in the handle_event callback you will pattern match on your Company select value and see if it changed from the one you have in your socket assigns. If it did, fetch the new Signs and re-assign. This will re-render your liveview automatically.

While there isn’t an example exactly like your use-case in it, phoenix_live_view_example is a good way to peek at how things work.

Later edit: found this (the “Bike comparison” example)

hendri-tobing

hendri-tobing

Thank you for the suggestion and example. I managed to populate the select field by using LiveView.

As suggested, I use phx-change that will pattern match the Company select value, then fetch the new Sign based on the value. Also assign the selected value to the Company select field.

This is part of my LiveView file.

  def render(assigns), do: LiveDeviceView.render("form.html", assigns)

  def mount(session, socket) do
    companies = Portal.Fleet.list_companies()
    selected_company = nil
    
    {:ok,
     assign(socket,
       changeset: Fleet.change_device(%Device{}),
       companies: companies,
       selected_company: selected_company,
       signs: [],
     )}
  end

  def handle_event("populate_sign", %{"device" => params}, socket) do
    signs = Fleet.get_company(String.to_integer(params["company_id"]))
    companies = Portal.Fleet.list_companies()
    changeset = Fleet.change_device(%Device{})
    selected_company = String.to_integer(params["company_id"])

    {:noreply, assign(socket, signs: signs, companies: companies, selected_company: selected_company, changeset: changeset)}
  end

form.html.leex

<%= f = form_for @changeset, "#", [phx_change: :populate_sign, phx_submit: :save] %>

  <%= label f, :company_id %>
  <%= select f, :company_id, Enum.map(@companies, &{&1.name, &1.id}), prompt: "Select a company", selected: @selected_company %>
  <%= error_tag f, :companies %>

  <%= if @signs == [] do %>
    <%= if @selected_company != nil do %> 
      <p>This company has no Sign</p> 
      <%= link "Add a new sign for this company", to: Routes.company_path(@socket, :new) %>
    <% end %>
  <% else %>
    <%= label f, :sign_id %>
    <%= select f, :sign_id, Enum.map(@signs, &{&1.name, &1.id}), prompt: "Select a sign" %>
    <%= error_tag f, :signs %>
  <% end %>

    <%= submit "Save", phx_disable_with: "Saving..." %>
  </div>

</form>

Thank you again for all responses.

Last Post!

vecuenca

vecuenca

Just chiming in several years later to say this helped me solve a similar frustrating issue I had. Thanks!

Where Next?

Popular in Questions Top

rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a &gt; b) do {:ok, "a"} end if (a &lt; b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New

Other popular topics Top

JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New

We're in Beta

About us Mission Statement