phoenixframework / phoenix_html

Building blocks for working with HTML in Phoenix

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Should Form.input_changed?/3 accept also string fields?

dvic opened this issue · comments

@spec input_changed?(t, t, atom) :: boolean()
def input_changed?(
%Form{impl: impl1, id: id1, name: name1, errors: errors1, source: source1} = form1,
%Form{impl: impl2, id: id2, name: name2, errors: errors2, source: source2} = form2,
field
)
when is_atom(field) do

Is there a reason why atoms are only accepted here? FormData.input_value accepts Field.t(), which is atom() | String.t(), right?

Dup of #408. :)

Is it a dup? Maybe I'm missing something here but that PR and discussion was about Form.fetch/2 right? This is about input_changes? and there's a discrepancy between the field type that is used in both Form.input_value/2 as well as FormData.input_value/3 (both Form.field()).

input_changed is only used by LV and only for atom Field, which is why I did the connection. But if you have external use cases for it then we can reevaluate!

So the usecase for us is that we're building a dynamic CMS where fields are defined at runtime, so we'd rather not use atoms but strings for the field names. But this is currently not possible as the following gist shows: https://gist.github.com/dvic/1b3d958c1ed9815ec8327cf42ffb13e3

When you click the button you get the following error (i.e., "foo" is not an atom):

19:22:28.058 [error] GenServer #PID<0.388.0> terminating
** (FunctionClauseError) no function clause matching in Phoenix.HTML.Form.input_changed?/3
    (phoenix_html 3.3.2) lib/phoenix_html/form.ex:197: Phoenix.HTML.Form.input_changed?(%Phoenix.HTML.Form{source: %{"foo" => "foo!"}, impl: Phoenix.HTML.FormData.Map, id: nil, name: nil, data: %{}, hidden: [], params: %{"foo" => "foo!"}, errors: [], options: [], index: nil, action: nil}, %Phoenix.HTML.Form{source: %{"foo" => "foo"}, impl: Phoenix.HTML.FormData.Map, id: nil, name: nil, data: %{}, hidden: [], params: %{"foo" => "foo"}, errors: [], options: [], index: nil, action: nil}, "foo")
    (phoenix_live_view 0.19.5) lib/phoenix_live_view/engine.ex:1234: Phoenix.LiveView.Engine.nested_changed_assign?/4
    liveview_access_bug.exs:50: anonymous fn/2 in Example.HomeLive.render/1
    (phoenix_live_view 0.19.5) lib/phoenix_live_view/diff.ex:363: Phoenix.LiveView.Diff.traverse/7
    (phoenix_live_view 0.19.5) lib/phoenix_live_view/diff.ex:538: anonymous fn/4 in Phoenix.LiveView.Diff.traverse_dynamic/7
    (elixir 1.15.4) lib/enum.ex:2510: Enum."-reduce/3-lists^foldl/2-0-"/3
    (phoenix_live_view 0.19.5) lib/phoenix_live_view/diff.ex:361: Phoenix.LiveView.Diff.traverse/7
    (phoenix_live_view 0.19.5) lib/phoenix_live_view/diff.ex:136: Phoenix.LiveView.Diff.render/3
Last message: %Phoenix.Socket.Message{topic: "lv:phx-F3ueTvL6-7mpBAGB", event: "event", payload: %{"event" => "change_item", "type" => "click", "value" => %{"value" => ""}}, ref: "8", join_ref: "4"}
State: %{socket: #Phoenix.LiveView.Socket<id: "phx-F3ueTvL6-7mpBAGB", endpoint: Example.Endpoint, view: Example.HomeLive, parent_pid: nil, root_pid: #PID<0.388.0>, router: Example.Router, assigns: %{form: %Phoenix.HTML.Form{source: %{"foo" => "foo"}, impl: Phoenix.HTML.FormData.Map, id: nil, name: nil, data: %{}, hidden: [], params: %{"foo" => "foo"}, errors: [], options: [], index: nil, action: nil}, items: %{"foo" => "foo"}, __changed__: %{}, flash: %{}, live_action: :index}, transport_pid: #PID<0.386.0>, ...>, components: {%{}, %{}, 1}, topic: "lv:phx-F3ueTvL6-7mpBAGB", serializer: Phoenix.Socket.V2.JSONSerializer, join_ref: "4", upload_names: %{}, upload_pids: %{}}

So it's not per se an external usecase, it's just that it doesn't work currently in liveview :) And I assume the above gist should work?

Especially because you have to define the keys as strings in the map when using to_form. I think it's really confusing for users that they then cannot use string fields as well in the @form[xxx] part.

Right, now we are going back to #408 which outlines why field does not allow strings. We can make it allow strings, but it should be clear there is no atom conversion.

Right, now we are going back to #408 which outlines why field does not allow strings. We can make it allow strings, but it should be clear there is no atom conversion.

Please bear with me, but I'm really confused now :D What do you mean with 'field does not allow strings'? I assume the "fix" you talk about in #408 was this commit 3b6e160? Here you explicitly allow strings right?

So this issue: phoenixframework/phoenix_live_view#2438 it mentions support for @form["field"], to be clear, this is what I want, it does not have to convert it to an atom for me, my FormData implementation will handle the details.

Oh wow, I completely forgot we actually merged it later on. So you are right, input_changed? should accept strings, correct.

Oh wow, I completely forgot we actually merged it later on. So you are right, input_changed? should accept strings, correct.

Cool! PR coming up ^^

Wait now I'm confused again about the implementation, because the errors are stored as keyword lists right? So we do have to do String.to_existing_atom/1? But this is not what you wanted, correct?

I mean, the docs mention this:

The field name can be either an atom or a string. If it is an atom,
it assumes the form keeps both data and errors as atoms. If it is a
string, it considers that data and errors are stored as strings for said
field.

So I should avoid Keyword.get_values when fetching errors in case of a string field and instead do a manual search?

Yes, a for-comprehension will do it fine (I think we do it in other places too).