maqnius

maqnius

Hey there :slight_smile:

I was trying to adapt the Contexts Article using the Form Struct and to_form.

I ended up having problems to render the input

<.input
      field={@form[:categories]}
      type="select"
      multiple={true}
      options={category_options(@article)}
    />

It errors like this

** (Protocol.UndefinedError) protocol Phoenix.HTML.Safe not implemented for type Ecto.Changeset (a struct).

because the value of the field @form[:categories] contains a Category changeset, introduced by the change_article\2 function - which is taken from the Context Article

  def change_article(%Article{} = article, attrs \\ %{}) do
    categories = list_categories_by_id(attrs["categories"])

    article
    |> Repo.preload(:categories)
    |> Article.changeset(attrs)
    |> Ecto.Changeset.put_assoc(:categories, categories)
  end

Phoenix tries to render the value in the select component but the changeset doesn’t help much here

  def input(%{type: "select"} = assigns) do
    ~H"""
    <fieldset class="fieldset mb-2">
      <label>
        <span :if={@label} class="fieldset-label mb-1">{@label}</span>
        <select
          id={@id}
          name={@name}
          class={["w-full select", @errors != [] && "select-error"]}
          multiple={@multiple}
          {@rest}
        >
          <option :if={@prompt} value="">{@prompt}</option>
          {Phoenix.HTML.Form.options_for_select(@options, @value)}
        </select>
      </label>
      <.error :for={msg <- @errors}>{msg}</.error>
    </fieldset>
    """
  end

I guess, I understand the problem, but I’m unsure how to proceed from here. I wasn’t very successfull finding examples using to_form.

What should I change, the input template, the form template, the change_article function or something else?

Thank you for help <3

Showing Posts 1 to 10

jdiago

jdiago

I think the select is supposed to be for @form[:category_ids].

The many_to_many :categories in the schema takes the categories key for itself so you can’t use it.

maqnius

maqnius OP

So would it be best to change the change_article function to populate category_ids?

def change_article(%Article{} = article, attrs \\ %{}) do
   article
   |> Article.changeset(attrs)
   |> Ecto.Changeset.put_change(:category_ids, attrs["category_ids"])
 end

That would make it harder to be re-used in the update and create functions, as I would have to parse the IDs in both of them

def update_article(%Article{} = article, attrs) do
    article
    |> change_article(attrs)
   # put_assoc from list of ids..
    |> Repo.update()
  end
jdiago

jdiago

attrs["categories"] there needs to be category_ids, too. list_categories_by_id and put_assoc are doing the work of wiring up the associated data. Read more about it here: Ecto.Changeset — Ecto v3.14.0

maqnius

maqnius OP

Mh yes, but the changeset returned by change_article ends up to build the form using to_form atm:

  defp apply_action(socket, :new, _params) do
    article = %Article{}

    socket
    |> assign(:page_title, "New Article")
    |> assign(:article, article)
    |> assign(:form, to_form(Catalog.change_article(article)))
  end

So category_ids has to be in the changeset (if I’m not missing anything).

But it also might just not work out this way, when m2m relations are involved.

Where would you inject category_ids into fhe form?

jdiago

jdiago

When you submit the form, part of the article params will be category_ids. When that goes into the create or update context functions, those will call change_article where it gets passed to list_categories_by_id and the result of that query gets shoved into the changeset by put_assoc.

maqnius

maqnius OP

Ahhh! Now I got it!

<.input
          field={@form[:category_ids]}
          type="select"
          multiple={true}
          options={category_options(@article)}
        />

@form[:category_ids] in this case merely defines the name of the submitted value not the value itself.

The actual submitted value is defined by the options={category_options(@article)} which renders to options.

I was wondering where :category_ids would be populated, as only the changeset with :categories is assigned (as a HTML.Form):

|> assign(:form, to_form(Catalog.change_article(article)))

Thank you very much for clearing things up! :folded_hands:

jdiago

jdiago

I think the confusion is coming from the relationship between the Phoenix.HTML.Form struct that comes from the to_form function and what you pass to it(in this case %Article{})?

In here Phoenix.HTML.Form — Phoenix.HTML v4.1.1 there’s this snippet:

It is possible to “access” fields which do not exist in the source data structure. A Phoenix.HTML.FormField struct will be dynamically created with some attributes such as name and id populated.

maqnius

maqnius OP

Yes :slight_smile: I noticed that it’s possible, but I didn’t understand when and how to make use of it.

The frameworks I used so far, expected to have a form data structure completely defined to render it properly and I treated the changeset as such in my mind.

jdiago

jdiago

In Phoenix, if you want to sketch out a form quickly, you can use to_form(%{}, as: "some_name"). You can then put whatever inputs to shape the params however you like.

LostKobrakai

LostKobrakai

I’d suggest reframing “article form setting categories” to “article form managing article_categories”, so moving from many to many to has many relationship. Then you can use inputs_for + select for category_id.

Where Next? Top

Trending in Questions Top

Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
psy-q
I’m trying to set up Emacs with elixir-ls via lsp-mode and credo via Flycheck. This should mostly be preconfigured as Flycheck picks up c...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews