I followed the tips suggested by @chrismccord in this PR to display some radio buttons as follows:
def render(assigns) do
~H"""
<p>Hello from radio buttons</p>
<.form for={@form}>
<div class="flex gap-x-2">
<%= for color <- ~w(gray red yellow green blue indigo pink purple) do %>
<.radio_group field={@form[:color]}>
<:radio value={color}><%= color %></:radio>
</.radio_group>
<% end %>
</div>
<input type="submit" value="Save" />
</.form>
"""
end
what raises some errors in the browser console saying:
Multiple IDs detected: color-0. Ensure unique element ids.
I added the radio_group
component function to the core_componenets.ex
module as follows:
attr :field, Phoenix.HTML.FormField, required: true
attr :rb_class, :string,
default: "rounded-lg text-zinc-900 focus:ring-0 sm:text-sm sm:leading-6"
slot :radio, required: true do
attr :value, :string, required: true
end
slot :inner_block
def radio_group(assigns) do
~H"""
<div>
<%= render_slot(@inner_block) %>
<div :for={{%{value: value} = rad, idx} <- Enum.with_index(@radio)}>
<label for={"#{@field.id}-#{idx}"}><%= render_slot(rad) %></label>
<input
type="radio"
name={@field.name}
id={"#{@field.id}-#{idx}"}
value={value}
checked={to_string(@field.value) == to_string(value)}
class={@rb_class}
/>
</div>
</div>
"""
end
When inspecting the generated HTML, I can see that it does contain multiple IDs with the same value and looks like this:
<div>
<div>
<label for="color-0">red</label>
<input type="radio" name="color" id="color-0" value="red" class="rounded-lg text-zinc-900 focus:ring-0 sm:text-sm sm:leading-6">
</div>
</div>
...
other radio inputs with the same `id="color-0"` value.
By the way, the above implementation does make it possible to provide any global attributes that might be updated.
Any ideas about that?
I think you need to move your for
to be only around the :radio
slot, Now you are rendering the whole group for every color with only a single color, hence the index is always zero. BTW I would add an id
to the .radio_group
and prefix that to the generated field id
. If not the in the off-chance that you would need to add a second .radio_group
on the same page of the same field (in different forms) you still get duplicate id’s.
Nope, it will fit if I move the for
inside of radio_group
:
<.form for={@form}>
<div class="flex gap-x-2">
<.radio_group field={@form[:color]}>
<%= for color <- ~w(gray red yellow green blue indigo pink purple) do %>
<:radio value={color}><%= color %></:radio>
<% end %>
</.radio_group>
</div>
<input type="submit" value="Save" />
The error:
== Compilation error in file lib/live_draft_web/live/radio_buttons_live.ex ==
** (Phoenix.LiveView.Tokenizer.ParseError) lib/live_draft_web/live/radio_buttons_live.ex:25:13: invalid slot entry <:radio>. A slot entry must be a direct child of a component
(phoenix_live_view 1.0.0-rc.6) lib/phoenix_live_view/tag_engine.ex:1376: Phoenix.LiveView.TagEngine.raise_syntax_error!/3
...
With slots you need to use :for
as slots need to be direct children of their component. They cannot be wrapped in eex tags.
2 Likes
Yay, that was it, thank you!
Here is the full working version:
<.form for={@form}>
<div class="flex gap-x-2">
<.radio_group field={@form[:color]}>
<:radio :for={color <- ~w(gray red yellow green blue indigo pink purple)} value={color}>
<%= color %>
</:radio>
</.radio_group>
</div>
<input type="submit" value="Save" />
</.form>
Just a drive-by curiosity: why is radio_group
with the dot syntax and radio
with the colon one? 
One notation is for function components and the other for their nested slots.
I have not kept up with the client-side Phoenix for a while, can you link me material that clarifies what do these two mean?
Ohhh I see, very nice actually. Thanks!
The only trick would be to pass in a custom class to every radio button, compared to the outdated version:
<div class="w-3/5 mb-6">
<%= label f, :color, class: "block mb-2 text-sm" %>
<div class="flex gap-x-2">
<%= for color <- ~w(gray red yellow green blue indigo pink purple) do %>
<label class="relative cursor-pointer" phx-target={@myself} phx-click="set_color" phx-value-color={color}>
<div class={"inline-block w-8 h-8 #{color}-bg rounded-full"}></div>
<%= radio_button f, :color, color, class: "hidden" %>
<%= if @current_color == color do %>
<span class="absolute z-10 inline-block w-4 h-4 text-white top-1 left-1/2 -translate-x-1/2">
<i class="fas fa-check"></i>
</span>
<% end %>
</label>
<% end %>
</div>
<%= error_tag f, :color %>
Maybe, it will be easier with Petal, - Forms (v2)