Fl4m3Ph03n1x
How to make checkbox work with form bindings Phoenix LV?
Background
I have a LiveView page where I have a small form. This form is supposed ton have a group of radio buttons, and a group of checkboxes.
To achieve this, I am trying to use Form Bindings:
Code
This is what I currently have. I want to give the user the option to receive notifications about some topics.
For the radio button group, the user chooses whether to receive the notification via email or sms. These are mutually exclusive.
For the checkbox group, the user chooses the topics. These are not mutually exclusive.
This is my html.heex file:
<.form for={@form} phx-submit="save">
<.input id={"email"} name="notifications" type="radio" value={"email"} field={@form[:notifications]} />
<.input id={"sms"} name="notifications" type="radio" value={"sms"} field={@form[:notifications]} />
<.input id={"sports"} name="topics" type="checkbox" value={"sports"} field={@form[:topics]} />
<.input id={"science"} name="topics" type="checkbox" value={"science"} field={@form[:topics]} />
<button>Save</button>
</.form>
And this is the corresponding LivewView:
defmodule MyApp.Settings do
use MyApp, :live_view
@impl true
def mount(_params, _session, socket) do
form = to_form(%{})
updated_socket = assign(socket, form: form)
{:ok, updated_socket}
end
def handle_event(event, params, socket) do
Logger.info("Event: #{inspect(event)} ; #{inspect(params)}")
{:noreply, socket}
end
end
If you are a keen reader, you will see I have no label, or legend tags anywhere. This is for simplification purposes.
Problem
Even though I get the correct value for the "notifications" radio group, the problem is that my checkboxes always return true or false:
[debug] HANDLE EVENT "save" in MyApp.Settings
Parameters: %{"notifications" => "sms", "topics" => "true"}
This is rather confusing. I was expecting something like:
Parameters: %{"notifications" => "sms", "topics" => ["sports", "science"]}
After reading the relevant parts of the docs, I don’t think the type checkbox is considered a special type, so it is not documented (in the link above mentioned).
I also don’t quite understand why I need a schema when to_form seems to work perfectly fine with %{}.
Questions
- How can I get a list of values instead of a boolean for checkboxes?
- I don’t use schemas in my application. Can
to_formwork with a Struct? - What would be the benefit of passing a Struct to
to_form?
Marked As Solved
Fl4m3Ph03n1x
After reading both Articles provided by @LostKobrakai I have arrived to a code that mixes what I learned from both. Definitely recommend the reading. Here are the code pieces for your inspiration:
core_components.ex
@doc """
Generate a checkbox group for multi-select.
## Examples
<.checkgroup
field={@form[:genres]}
label="Genres"
options={[%{name: "Fantasy", id: "fantasy"}, %{name: "Science Fiction", id: "sci-fi"}]}
selected={[%{name: "Fantasy", id: "fantasy"}]}
/>
"""
attr :id, :any
attr :name, :any
attr :label, :string, default: nil
attr :field, Phoenix.HTML.FormField, doc: "a form field struct retrieved from the form, for example: @form[:genres]"
attr :errors, :list
attr :required, :boolean, default: false
attr :options, :list, doc: "the options to pass to Phoenix.HTML.Form.options_for_select/2"
attr :rest, :global, include: ~w(disabled form readonly)
attr :class, :string, default: nil
attr :selected, :any, default: [],
doc: "the currently selected options, to know which boxes are checked"
def checkgroup(assigns) do
new_assigns =
assigns
|> assign(:multiple, true)
|> assign(:type, "checkgroup")
input(new_assigns)
end
def input(%{type: "checkgroup"} = assigns) do
~H"""
<div class="mt-2">
<%= for opt <- @options do %>
<div class="relative flex gap-x-3">
<div class="flex h-6 items-center">
<input id={opt.id} name={@name} type="checkbox" value={opt.id} class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600" checked={opt in @selected}>
</div>
<div class="text-sm leading-6">
<label for={opt.id} class="text-base font-semibold text-gray-900"><%= opt.name %></label>
</div>
</div>
<% end %>
</div>
"""
end
usage_live.html.heex
<.simple_form for={@form} phx-change="change" phx-submit="execute">
<div class="mt-4">
<.checkgroup field={@form[:genres]} label="Genres" options={@all_genres} selected={@selected_genres} required/>
</div>
<div class="mt-4">
<.button}>Execute Command</.button>
</div>
</.simple_form>
Also Liked
LostKobrakai
The built in <.input type="checkbox"> is for selfstanding checkboxes (boolean on the backend), not for multi value fields. Phoenix doesn’t come with helpers for a set of checkboxes.
See Making a CheckboxGroup Input · The Phoenix Files or https://kobrakai.de/scratchpad/checkboxes on approaches to handling such.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









