I am trying to build an admin panel - in one field I want a simple dropdown menu.
I found a way to do this, but I think I found a round-about way and hoping there is a straight forward way.
Here is what I did:
- add all the users to the assigns (so I could list them in the dropdown)
- hide the normal field that submits the user_id (but keep the value there) so that on save the value is submitted (probably possible to do from the select, but I couldn’t figure out how).
- add a select with all the users (and with the initial ‘selected’ value the same as the user_id in the form.
- wrote a handler to update the user_id in the changeset when the selected value changes.
Here is the code (for better or worse - but it works):
def render(assigns) do
~H"""
<div>
<.header>
<%= @title %>
<:subtitle>Use this form to manage domains records in your database.</:subtitle>
</.header>
<.simple_form
for={@form}
id="domains-form"
phx-target={@myself}
phx-change="validate"
phx-submit="save"
>
<.input field={@form[:domain_name]} type="text" label="Domain name" />
<.input field={@form[:domain_url]} type="text" label="Domain URL" />
<.input field={@form[:domain_descriptioin]} type="text" label="Domain descriptioin" />
<%!-- <.input field={@form[:user_id]} type="number" label="User ID" /> --%>
<.input field={@form[:user_id]} type="hidden" />
<label for="user_id">User</label>
<select name="user_id" id="user_id" phx-change="select_user">
<%= for user <- @users do %>
<%= if user.id == @domains.user_id do %>
<option value={user.id} selected><%= user.username %></option>
<% else %>
<option value={user.id}><%= user.username %></option>
<% end %>
<%!-- <option value={user.id}><%= user.username %></option> --%>
<%!-- <% selected = if user.id == @form[:user_id], do: "selected", else: "" %> --%>
<%!-- <option value={user.id} {selected}><%= user.username %></option> --%>
<% end %>
</select>
<:actions>
<.button phx-disable-with="Saving...">Save Domains</.button>
</:actions>
</.simple_form>
</div>
"""
end
@impl true
def update(%{domains: domains} = assigns, socket) do
# add the full list of users for the dropdown selector
users = ClearSync.Core.Accounts.list_users()
socket = assign(socket, users: users)
changeset = WorkDomains.change_domains(domains)
{:ok,
socket
|> assign(assigns)
|> assign_form(changeset)}
end
# update the user chosen in the changeset when the selector changes who is selected
def handle_event("select_user", %{"user_id" => user_id}, socket) do]
changeset =
socket.assigns.domains
|> WorkDomains.change_domains(%{"user_id" => String.to_integer(user_id)})
# |> WorkDomains.change_domains(%{"user_id" => parsed_user_id})
{:noreply, assign_form(socket, changeset)}
end
This works, but
- it seems clumsy, I am assuming I have overlooked something fundamental
- my hidden .input field is hidden, but takes up visible white space
Note: I haven’t caught up on understanding the newest LiveView changes - I am sure my approach reflects that, in addition to helping me cleanup the whitespace and create a more elegant solution, If its clear what fundamental misunderstanding(s) I have - I would appreciate knowing where I can learn more - I will try to go through some liveview 1.7 tutorials, but if there is a short cut short of starting over, I would appreciate that.
Thanks,
Bill