thread

thread

Hello! I’ve been racking my brain on this one, and I’d love to hear any suggestions.

Essentially, I am looking to build a form where “name” and “game wins count” can be edited for each of a series of teams. The tricky part is that the team names are only known at runtime.

When name is a string, ~H"<%= text_input(f, @name) %>" (or input_value) errors:

** (ArgumentError) expected field to be an atom, got: "Unstoppables[wins]"

It would be great if I could somehow use changesets and/or Phoenix.HTML.Form stuff to pull this off with orderly error messages, etc, but I’m increasingly feeling I’ll need to just make my own input tags and handle everything myself.

I noticed the older Phoenix only supported atom for t:Phoenix.HTML.Form.field/0 but the latest has atom | String.t() for the type. I admit, I’m still not clear where string fields come in.

Btw, I found this article which builds a custom embedded_schema, but alas, it depends on knowing the field names (teams in my case) at compile time, which I do not. Dynamic fields from conf to UI in Elixir and Phoenix

Seems like Phoenix may not have tooling for me in this case :slight_smile:

Thanks so much for any thoughts or advice!

Showing Posts 1 to 3

soup

soup

text_input accepts extra options which are passed down to the tag, so you can set your custom name that way, etc. I have some very old code that calls String.to_atom when generating a dynamic field but there is a memory pressure risk in doing that (atoms are never gc’d, in my case I am OK with this as there’s a fixed bound on what strings will be passed in – but they dont yet exist so to_existing_atoms doesn’t fit – but in your case where strings are free-input I would not do this.). I don’t believe new code should need this when using the newer form/field & components.

I think if you set your name to something like text_input(form, :not_used, name: "team[#{team_name}][wins]") you might get to where you want to go? I would look at see what field actually does when you’re providing your own name (& id?), you may find it has no impact just passing the same atom for each input.

Phoenix.HTML.Form is pretty relaxed and will create fields for anything you Access – even if it does not exist in the underlying changeset – which may be helpful. Eg form[Unstoppables_wins] should give you a %Field{} where name is something reasonable, but I think you probably want to use an embedded changeset and inputs_for.

  defmodule TournamentResult do
    use Ecto.Schema
    import Ecto.Changeset

    embedded_schema do
      field :tournament_name, :string

      embeds_many :teams, Team do
        field :name, :string
        field :wins, :integer

        # need this for cast embed
        def changeset(team, params) do
          team
          |> cast(params, [:name, :wins])
        end
      end
    end

    def changeset(tournament \\ %__MODULE__{}, params) do
      tournament
      |> cast(params, [:tournament_name])
      |> cast_embed(:teams, required: true)
    end
  end

    <.form
      for={@form}
      id="tournament_form"
      phx-change="validate"
      phx-submit="submit"
      phx-auto-recover="recover"
    >
      <.inputs_for :let={team} field={@form[:teams]}>
            <!-- the core components probably can just accept the field directly, this was
                  rough hewn from a legacy project that doesnt have them but has been updated to 1.7
                  which is why I set the name and value directly.
                 
                  You also probably want a hidden input that retains the team id (ecto should generate one)  -->
              <input
                type="text"
                placeholder="Team Name"
                autocomplete="off"
                class={[
                  "w-full",
                  team[:name].errors != [] && "placeholder-red-300 bg-red-50"
                ]}
                name={team[:name].name}
                value={team[:name].value}
              />
            <!-- still generates "valid" field even though does not exist in model -->
            <%= inspect team[:some_fake_field].name %>

Just be aware that when converted to “params” your teams change from an array to a map of numeric strings ("0" => team_1) so you lose intrinsic ordering. It seems that the newer to_form accounts for this better than it used to, but I have code there to re-index things they are added or removed, may not be needed now but just a heads up.

Not sure if all that helps you. Honestly I’m a bit unclear on what part is hard, Are you able to provide a basic (non-functioning) repo as an example? Are you using LV or regular controllers?

thread

thread OP

Thank you so, so much for this reply, @soup! I think it may just be what I needed to get going. Will experiment with this in the next couple days and reply back how it goes.

Thank you !!

thread

thread OP

Your post was super helpful, @soup. inputs_for injects hidden id fields which were very important for the server to associate each subgroup to the original data. My data itself didn’t actually have ids, but I assigned each group an id anyway and used that in the form. The changeset was being maintained in the LiveView state and when the form params came in, Ecto.Changeset was able to associate them properly.

This was more straightforward than I was thinking, indeed.

Thanks again! I very much appreciate your thoughtful response here.

— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
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
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
mnkhod
So i have been using ash framework for a while and i love it. However currently the issue im having with ash framework is the error handl...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
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
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews