Hey @loics2 ,
Thanks for posting your solution as well.
I found a way to do it without a virtual field and additional decoration of the ecto data.
The error you described pops up as you described when you click a select. This is because in that event the “value” assign is suddenly a list of Ecto.Changeset
. This changeset contains the updated tags in a data
field, so you can use these to figure out the selection.
And in some update cycles the field value is not even a changeset, but only a list of strings (the selected ids).
So I wrote a helper function to pick the right values:
defp pick_selected( assigns ) do
assigns.value
|> Enum.map(fn x->
case x do
%Ecto.Changeset{ action: :update, data: data } ->
data.id
%Ecto.Changeset{} -> nil
%{id: id} ->
id
x when is_binary(x) -> if x == "", do: nil, else: x
_ -> nil
end
end)
|> Enum.filter( &!is_nil(&1))
end
And this is how you use it, first line in described checkgroup:
def input(%{type: "checkgroup"} = assigns) do
assigns = assign( assigns, :selected, pick_selected(assigns) )
~H"""
<div phx-feedback-for={@name} class="text-sm">
<.label for={@id}><%= @label %></.label>
<div class="mt-1 w-full bg-white border-gray-300 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
<div class="grid grid-cols-1 gap-1 text-sm items-baseline">
<input
type="hidden"
name={@name}
value=""
/>
<div class="flex items-center" :for={{label, value} <- @options}>
<label
for={"#{@name}-#{value}"} class="font-medium text-gray-700">
<input
type="checkbox"
id={"#{@name}-#{value}"}
name={@name}
value={value}
checked={value in @selected}
class="mr-2 h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-500 transition duration-150 ease-in-out"
{@rest}
/>
<%= label %>
</label>
</div>
</div>
</div>
<.error :for={msg <- @errors}><%= msg %></.error>
</div>
"""
end