-
Notifications
You must be signed in to change notification settings - Fork 900
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Form serialization is hard, kinda (#3069)
So... When trying to fix #3056, Chris changed the way the parameters are built in serializeForm. This broke the way we were handling select elements with the "multiple" option. I tried to fix this in #3067, but actually broke Chris' code. This commit refactors the serializeForm function to actually add a temporary input element to the form to properly encode the values using the `FormData` API. This fixes the issue with the multiple select and also ensure that we send the value of the submitter at the correct position. Also adds some tests that were harder to build than I'd like to admit, because doing Ecto sort_param/drop_param stuff without Ecto is not trivial... Thanks for reading!
- Loading branch information
Showing
5 changed files
with
267 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
defmodule Phoenix.LiveViewTest.E2E.FormDynamicInputsLive do | ||
use Phoenix.LiveView | ||
|
||
alias Phoenix.LiveView.JS | ||
|
||
@impl Phoenix.LiveView | ||
def mount(params, _session, socket) do | ||
{:ok, | ||
socket | ||
|> assign_form(%{}) | ||
|> assign(:checkboxes, params["checkboxes"] == "1") | ||
|> assign(:submitted, false)} | ||
end | ||
|
||
defp assign_form(socket, params) do | ||
form = | ||
Map.take(params, ["name"]) | ||
|> Map.put( | ||
"users", | ||
build_users( | ||
params["users"] || %{}, | ||
params["users_sort"] || [], | ||
params["users_drop"] || [] | ||
) | ||
) | ||
|> to_form(as: :my_form, id: "my-form", default: []) | ||
|
||
assign(socket, :form, form) | ||
end | ||
|
||
defp build_users(value, sort, drop) do | ||
{sorted, pending} = | ||
if is_list(sort) do | ||
Enum.map_reduce(sort -- drop, value, &Map.pop(&2, &1, %{"name" => nil})) | ||
else | ||
{[], value} | ||
end | ||
|
||
result = | ||
sorted ++ | ||
(pending | ||
|> Map.drop(drop) | ||
|> Enum.map(&key_as_int/1) | ||
|> Enum.sort() | ||
|> Enum.map(&elem(&1, 1))) | ||
|
||
Enum.with_index(result) | ||
|> Map.new(fn {item, i} -> {to_string(i), item} end) | ||
end | ||
|
||
defp key_as_int({key, val}) when is_binary(key) and byte_size(key) < 32 do | ||
case Integer.parse(key) do | ||
{key, ""} -> {key, val} | ||
_ -> {key, val} | ||
end | ||
end | ||
|
||
@impl Phoenix.LiveView | ||
def handle_event("validate", %{"my_form" => params}, socket) do | ||
{:noreply, assign_form(socket, params)} | ||
end | ||
|
||
def handle_event("save", %{"my_form" => params}, socket) do | ||
socket | ||
|> assign_form(params) | ||
|> assign(:submitted, true) | ||
|> then(&{:noreply, &1}) | ||
end | ||
|
||
@impl Phoenix.LiveView | ||
def render(assigns) do | ||
~H""" | ||
<.form | ||
for={@form} | ||
phx-change="validate" | ||
phx-submit="save" | ||
style="display: flex; flex-direction: column; gap: 4px; max-width: 500px;" | ||
> | ||
<input | ||
type="text" | ||
id={@form[:name].id} | ||
name={@form[:name].name} | ||
value={@form[:name].value} | ||
placeholder="name" | ||
/> | ||
<.inputs_for :let={ef} field={@form[:users]} default={[]}> | ||
<div style="padding: 4px; border: 1px solid gray;"> | ||
<input type="hidden" name="my_form[users_sort][]" value={ef.index} /> | ||
<input | ||
type="text" | ||
id={ef[:name].id} | ||
name={ef[:name].name} | ||
value={ef[:name].value} | ||
placeholder="name" | ||
/> | ||
<button | ||
:if={!@checkboxes} | ||
type="button" | ||
name="my_form[users_drop][]" | ||
value={ef.index} | ||
phx-click={JS.dispatch("change")} | ||
> | ||
Remove | ||
</button> | ||
<label :if={@checkboxes}> | ||
<input type="checkbox" name="my_form[users_drop][]" value={ef.index} /> Remove | ||
</label> | ||
</div> | ||
</.inputs_for> | ||
<input type="hidden" name="my_form[users_drop][]" /> | ||
<button | ||
:if={!@checkboxes} | ||
type="button" | ||
name="my_form[users_sort][]" | ||
value="new" | ||
phx-click={JS.dispatch("change")} | ||
> | ||
add more | ||
</button> | ||
<label :if={@checkboxes}> | ||
<input type="checkbox" name="my_form[users_sort][]" /> add more | ||
</label> | ||
</.form> | ||
<p :if={@submitted}>Form was submitted!</p> | ||
""" | ||
end | ||
end |