Fl4m3Ph03n1x

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

Fl4m3Ph03n1x

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.

Where Next?

Popular Frontend topics Top

davearonson
I’m unit-testing some JS, with Jasmine, and I’d like to check our coverage. We’re not using any front-end framework, nor much JS, so no ...
New
ClaudiaGiv
I have a sveltekit project that is using GoTrue library for authentication. In development mode (npm run dev) everything works but when I...
New
palak231
Hi this is Palak Sharma I am new here and I found this community while researching about JavaScript over the internet. Well I have comp...
/js
New
pavanforza
I have a requirement to extract data from firebase which is used to build serverless applications. Can we connect Firebase no-sql databa...
New
sona11
I was working on a project that required me to update data in a SQL database. I initiated a transaction and modified the data with a seri...
New
prego4444
how can i make a border like this to be exactly on the midle of the edge? i could only found border in inside and outside but nothing on ...
New
Fl4m3Ph03n1x
Background I have Phoenix umbrella application. When inside said application, I can run it without issues if MIX_ENV=prod. However, if I ...
New
Julien0577
Hi all, Anybody knows how to do this menu animation? (from BBVA APP, they have the same for both android and iOS app). Is it custom?...
New
hosseinkhosromanesh
hello , i should code a cluster like image bellow we have no challenge in coding backend but in front need some clue to do this its a dy...
/js
New
Arpeggio
I have the following HTML structure, which is dynamically rendered from a Sightly (HTL) page in a new AEM component we’re building, so I ...
New

Other popular topics Top

Devtalk
Hello Devtalk World! Please let us know a little about who you are and where you’re from :nerd_face:
New
AstonJ
Or looking forward to? :nerd_face:
487 12657 259
New
brentjanderson
Bought the Moonlander mechanical keyboard. Cherry Brown MX switches. Arms and wrists have been hurting enough that it’s time I did someth...
New
AstonJ
Thanks to @foxtrottwist’s and @Tomas’s posts in this thread: Poll: Which code editor do you use? I bought Onivim! :nerd_face: https://on...
New
Exadra37
I am asking for any distro that only has the bare-bones to be able to get a shell in the server and then just install the packages as we ...
New
Exadra37
Oh just spent so much time on this to discover now that RancherOS is in end of life but Rancher is refusing to mark the Github repo as su...
New
mafinar
This is going to be a long an frequently posted thread. While talking to a friend of mine who has taken data structure and algorithm cou...
New
AstonJ
We’ve talked about his book briefly here but it is quickly becoming obsolete - so he’s decided to create a series of 7 podcasts, the firs...
New
PragmaticBookshelf
Author Spotlight: VM Brasseur @vmbrasseur We have a treat for you today! We turn the spotlight onto Open Source as we sit down with V...
New
CommunityNews
A Brief Review of the Minisforum V3 AMD Tablet. Update: I have created an awesome-minisforum-v3 GitHub repository to list information fo...
New