Fl4m3Ph03n1x
Assigns is not assigning attribute in custom core component
Background
I have a custom component in my LiveView, which is basically a group of checkboxes.
I want to add a new attribute to my custom component, however no matter what I do, I always get nil when accessing said attribute.
Code
core_components.ex
@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 :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 :rest, :global, include: ~w(form readonly)
attr :class, :string, default: nil
attr :options, :list, default: [], doc: "the options to pass to Phoenix.HTML.Form.options_for_select/2"
attr :selected, :list, default: [], doc: "the currently selected options, to know which boxes are checked"
attr :meow, :any, default: "meow", doc: "a very cool new attribute!"
def checkgroup(assigns) do
new_assigns =
assigns
|> assign(:multiple, true)
|> assign(:type, "checkgroup")
input(new_assigns)
end
def input(%{type: "checkgroup"} = assigns) do
~H"""
<p> <%= "Cat says: #{@meow}" %></p>
<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} checked={opt in @selected} disabled={false} />
</div>
<div class="text-sm leading-6">
<label for={opt.id} ><%= opt.name %></label>
</div>
</div>
<% end %>
</div>
"""
end
Here I am using a customer component to render a stylish checkbox group. It works fine, except for the :meow attribute, which somehow is always nil when inside the input function.
Error
Using this code as a sample:
<.checkgroup field={@form[:my_form]} label="SUper question!" options={@sounds} selected={@selected_sounds} meow={[1, 2]} required />
I get the following error upon rendering this code:
** (exit) an exception was raised:
** (KeyError) key :meow not found in: %{
__changed__: nil,
__given__: %{
__changed__: nil,
__given__: %{
__changed__: nil,
__given__: %{
__changed__: nil,
field: %Phoenix.HTML.FormField{
id: "my_form",
name: "my_form",
errors: [],
field: :my_form,
form: %Phoenix.HTML.Form{
source: %{"some_command" => []},
impl: Phoenix.HTML.FormData.Map,
id: nil,
name: nil,
data: %{},
hidden: [],
params: %{"some_command" => []},
errors: [],
options: [],
index: nil,
action: nil
},
value: nil
},
label: "SUper question!",
meow: [1, 2],
options: [1, 2, 3, 4],
# .... more things follow
As you can see, meow: [1, 2], is clearly present. Yet, @meow returns nil and using assigns.meow outright crashes the application.
What am I doing wrong here and how can I fix it?
Marked As Solved
Fl4m3Ph03n1x
After a lot of digging around, i realized that all of the input functions deal with assigns. This meant that some things are being done that I dont fully understand, not control.
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.
Also Liked
sodapopcan
Crap, sorry, I was working on a response to this a few days ago and got distracted
Sorry about that! Glad you solved it!
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








