Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build form data using Plug.Conn.Query (remove DeepMerge) #94

Merged
merged 1 commit into from
Jul 5, 2024

Conversation

ftes
Copy link
Contributor

@ftes ftes commented Jun 18, 2024

Fixes #77
Replaces #79, #90, #93

Goal

Ensure same form handling semantics as Phoenix controllers and LiveView.

Details

Track form data as a flat list of {name, value} tuples.
This can be read straight from the DOM and needs no further conversions.
Example:

form_data = [
  {"user[name]", "Ada"},
  {"user[subscribe][]", "email"},
  {"user[subscribe][]", "push"}
]

Defer transforming that flat list into a nested data structure: When submitting form or evaluating other LiveLive phx-* callbacks.

Use Plug.Conn.Query.decode/4 to transform flat list into nested data structure. Reason: Phoenix Controllers and LiveView do the same (see #77 (comment)).

def build_data(data) when is_list(data) do
data
|> Enum.map_join("&", fn {key, value} ->
"#{URI.encode_www_form(key)}=#{if(value, do: URI.encode_www_form(value))}"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to skip the encode/decode cycle.
But that's the only public interfacePlug.Conn.Query seems to offer.

On the other hand, it seems reasonable to use a standardized format (application/x-www-form-urlencoded) as the interface.

@soundmonster
Copy link
Contributor

This PR does sound like a good idea to me. I tried supporting multiple inputs with ending with [] using DeepMerge, and being able to also uncheck a checkbox from a group made the whole thing super complex. I tried backporting the tests from my branch on yours and they don't go through, but I guess I'll be able to get it to work on top.

Copy link
Owner

@germsvel germsvel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for opening this PR! 🥳

I was out for a little while, so I didn't have a chance to look at it til now.

I love this work. In my mind, this was the next logical step. we dealt with deep merging of data because of how we originally supported map data being passed for forms with fill_form.

But since we've moved to using labels, it makes sense we keep the names flat as much as possible and encode/decode that stuff only when we need to send it to the server. So, I'm glad to see this coming through! 👍

I had one question about a test that was modified (see the comment on that question), and I want to play around locally with this branch a little more before merging. But I think this is almost ready to go.

Thanks again!

|> Enum.reduce(value, fn key, acc ->
%{key => acc}
end)
end
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥 🔥 🔥

@@ -36,7 +36,6 @@ defmodule PhoenixTest.MixProject do
defp deps do
[
{:ex_doc, "~> 0.31", only: :dev, runtime: false},
{:deep_merge, "~> 1.0"},
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

@@ -451,11 +451,11 @@ defmodule PhoenixTest.StaticTest do
|> assert_has("#form-data", text: "race_2: [elf,dwarf]")
end

test "honors empty default for multi select", %{conn: conn} do
test "contains no data for empty multi select", %{conn: conn} do
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the "honoring" of an empty multi select was intentionally introduced in f146186. Can you confirm that's no longer needed? Or will this solution equivalent?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I see, f146186 was mainly about accepting the boolean multiple attribute (and not required multiple="multiple".

I think this test, added in the same PR, was slightly incorrect.

  • A HTML select (not multiple) submits the first option by default, even without user input.
  • A HTML multiple select submits nothing by default.

Quick demonstration: Add multiple to the Try it select tag: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select

In a real world example, you would probably add a hidden input with empty value before the multi select.
To ensure the value can be cleared. Much like for checkboxes.

Best if @soundmonster confirms this test should be changed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes can confirm that clearing multi-select values only works if an explicitly empty value is hidden next to it, just like with checkboxes 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you agree it is correct to change the test? @soundmonster

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, absolutely!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then let's get this merged @germsvel , wdyt?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! 🎉 Thanks for checking on this.

@germsvel germsvel merged commit 2962826 into germsvel:main Jul 5, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support multiple checkboxes
3 participants