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?