Skip to content

Commit

Permalink
allow to skip persistent id generation
Browse files Browse the repository at this point in the history
Fixes #3673
  • Loading branch information
SteffenDE committed Feb 13, 2025
1 parent abad050 commit ab45d79
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 22 deletions.
58 changes: 42 additions & 16 deletions lib/phoenix_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2710,6 +2710,14 @@ defmodule Phoenix.Component do
"""
)

attr.(:skip_persistent_id, :boolean,
default: false,
doc: """
Skip the automatic rendering of hidden _persistent_id fields used for reordering
inputs.
"""
)

attr.(:options, :list,
default: [],
doc: """
Expand All @@ -2732,11 +2740,42 @@ defmodule Phoenix.Component do
|> Keyword.merge(assigns.options)

forms = parent_form.impl.to_form(parent_form.source, parent_form, field_name, options)

forms =
case assigns do
%{skip_persistent_id: true} ->
forms

_ ->
apply_persistent_id(
parent_form,
forms,
field_name,
options
)
end

assigns = assign(assigns, :forms, forms)

~H"""
<%= for finner <- @forms do %>
<%= if !@skip_hidden do %>
<%= for {name, value_or_values} <- finner.hidden,
name = name_for_value_or_values(finner, name, value_or_values),
value <- List.wrap(value_or_values) do %>
<input type="hidden" name={name} value={value} />
<% end %>
<% end %>
{render_slot(@inner_block, finner)}
<% end %>
"""
end

defp apply_persistent_id(parent_form, forms, field_name, options) do
seen_ids = for f <- forms, vid = f.params[@persistent_id], into: %{}, do: {vid, true}
acc = {seen_ids, 0}

{forms, _} =
Enum.map_reduce(forms, acc, fn
Enum.map_reduce(forms, {seen_ids, 0}, fn
%Phoenix.HTML.Form{params: params} = form, {seen_ids, index} ->
id =
case params do
Expand Down Expand Up @@ -2765,20 +2804,7 @@ defmodule Phoenix.Component do
{new_form, {Map.put(seen_ids, id, true), index + 1}}
end)

assigns = assign(assigns, :forms, forms)

~H"""
<%= for finner <- @forms do %>
<%= if !@skip_hidden do %>
<%= for {name, value_or_values} <- finner.hidden,
name = name_for_value_or_values(finner, name, value_or_values),
value <- List.wrap(value_or_values) do %>
<input type="hidden" name={name} value={value} />
<% end %>
<% end %>
{render_slot(@inner_block, finner)}
<% end %>
"""
forms
end

defp next_id(idx, %{} = seen_ids) do
Expand Down
40 changes: 34 additions & 6 deletions test/phoenix_component/components_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ defmodule Phoenix.LiveView.ComponentsTest do

template = ~H"""
<.form :let={f} as={:myform}>
<.inputs_for :let={finner} field={f[:inner]} }>
<.inputs_for :let={finner} field={f[:inner]}>
<% 0 = finner.index %>
<input id={finner[:foo].id} name={finner[:foo].name} type="text" />
</.inputs_for>
Expand All @@ -526,7 +526,7 @@ defmodule Phoenix.LiveView.ComponentsTest do

template = ~H"""
<.form :let={f} as={:myform}>
<.inputs_for :let={finner} field={f[:inner]} } id="test" as={:name}>
<.inputs_for :let={finner} field={f[:inner]} id="test" as={:name}>
<input id={finner[:foo].id} name={finner[:foo].name} type="text" />
</.inputs_for>
</.form>
Expand All @@ -542,7 +542,7 @@ defmodule Phoenix.LiveView.ComponentsTest do

template = ~H"""
<.form :let={f} as={:myform}>
<.inputs_for :let={finner} field={f[:inner]} } as={:name}>
<.inputs_for :let={finner} field={f[:inner]} as={:name}>
<input id={finner[:foo].id} name={finner[:foo].name} type="text" />
</.inputs_for>
</.form>
Expand All @@ -562,7 +562,7 @@ defmodule Phoenix.LiveView.ComponentsTest do

template = ~H"""
<.form :let={f} as={:myform}>
<.inputs_for :let={finner} field={f[:inner]} } default={%{foo: "123"}}>
<.inputs_for :let={finner} field={f[:inner]} default={%{foo: "123"}}>
<input id={finner[:foo].id} name={finner[:foo].name} type="text" value={finner[:foo].value} />
</.inputs_for>
</.form>
Expand All @@ -585,7 +585,6 @@ defmodule Phoenix.LiveView.ComponentsTest do
<.inputs_for
:let={finner}
field={f[:inner]}
}
default={[%{foo: "456"}]}
prepend={[%{foo: "123"}]}
append={[%{foo: "789"}]}
Expand Down Expand Up @@ -613,7 +612,7 @@ defmodule Phoenix.LiveView.ComponentsTest do

template = ~H"""
<.form :let={f} as={:myform}>
<.inputs_for :let={finner} field={f[:inner]} } options={[foo: "bar"]}>
<.inputs_for :let={finner} field={f[:inner]} options={[foo: "bar"]}>
<p>{finner.options[:foo]}</p>
</.inputs_for>
</.form>
Expand All @@ -623,6 +622,35 @@ defmodule Phoenix.LiveView.ComponentsTest do
assert [p] = Floki.find(html, "p")
assert Floki.text(p) =~ "bar"
end

test "can disable persistent ids" do
assigns = %{}

template = ~H"""
<.form :let={f} as={:myform}>
<.inputs_for
:let={finner}
field={f[:inner]}
default={[%{foo: "456"}, %{foo: "789"}]}
prepend={[%{foo: "123"}]}
append={[%{foo: "101112"}]}
skip_persistent_id
>
<input id={finner[:foo].id} name={finner[:foo].name} type="text" value={finner[:foo].value} />
</.inputs_for>
</.form>
"""

assert t2h(template) ==
~X"""
<form>
<input id="myform_inner_0_foo" name="myform[inner][0][foo]" type="text" value="123"></input>
<input id="myform_inner_1_foo" name="myform[inner][1][foo]" type="text" value="456"></input>
<input id="myform_inner_2_foo" name="myform[inner][2][foo]" type="text" value="789"></input>
<input id="myform_inner_3_foo" name="myform[inner][3][foo]" type="text" value="101112"></input>
</form>
"""
end
end

describe "live_file_input/1" do
Expand Down

0 comments on commit ab45d79

Please sign in to comment.