After a lot of digging around, i realized that all of the input functions incore_components.ex modify assigns. The best example of this is this intermediary function, called before any input component functions:
def input(%{field: %Phoenix.HTML.FormField{} = field} = assigns) do
assigns
|> assign(field: nil, id: assigns.id || field.id)
|> assign(:errors, Enum.map(field.errors, &translate_error(&1)))
|> assign_new(:name, fn -> if assigns.multiple, do: field.name <> "[]", else: field.name end)
|> assign_new(:value, fn -> field.value end)
|> input()
end
This means I do not fully control what is going on. I decided to move away from using/modifying the input functions and created my own component from scratch:
@doc """
Generate a checkbox group for multi-select.
## Examples
<.checkgroup
field={@form[:genres]}
label="Genres"
options={[{"Fantasy", "fantasy"}, {"Science Fiction", "sci-fi"}]}
selected={[{"Fantasy", "fantasy"}]}
/>
"""
attr :name, :any
attr :field, Phoenix.HTML.FormField,
doc: "a form field struct retrieved from the form, for example: @form[:genres]"
attr :required, :boolean, default: false
attr :options, :list,
default: [],
doc: "the options to pass to Phoenix.HTML.Form.options_for_select/2"
attr :disabled, :list, default: [], doc: "the list of options that are disabled"
attr :selected, :list,
default: [],
doc: "the currently selected options, to know which boxes are checked"
def checkgroup(%{field: %Phoenix.HTML.FormField{} = field} = assigns) do
assigns =
assigns
|> assign(:name, "#{field.name}[]")
~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={
if opt in @disabled do
"h-4 w-4 rounded border-gray-300 text-gray-300 focus:ring-indigo-600"
else
"h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600"
end
}
checked={opt in @selected}
disabled={opt in @disabled}
/>
</div>
<div class="text-sm leading-6">
<label
for={opt.id}
class={
if opt in @disabled do
"text-base font-semibold text-gray-300"
else
"text-base font-semibold text-gray-900"
end
}
>
<%= opt.name %>
</label>
</div>
</div>
<% end %>
</div>
"""
end
which works as expected and all the assigns not have the expected values.