Skip to content

Commit

Permalink
Merge pull request #439 from phoenixframework/cm-form-action-changed
Browse files Browse the repository at this point in the history
Add form action and consider input changed if action changes
  • Loading branch information
chrismccord committed Feb 29, 2024
2 parents c50f33d + 323a65a commit adf1c38
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 6 deletions.
28 changes: 22 additions & 6 deletions lib/phoenix_html/form.ex
Expand Up @@ -64,6 +64,7 @@ defmodule Phoenix.HTML.Form do
id: nil,
name: nil,
data: nil,
action: nil,
hidden: [],
params: %{},
errors: [],
Expand All @@ -74,6 +75,7 @@ defmodule Phoenix.HTML.Form do
source: Phoenix.HTML.FormData.t(),
name: String.t(),
data: %{field => term},
action: atom(),
params: %{binary => term},
hidden: Keyword.t(),
options: Keyword.t(),
Expand Down Expand Up @@ -191,18 +193,32 @@ defmodule Phoenix.HTML.Form do
@doc """
Receives two forms structs and checks if the given field changed.
The field will have changed if either its associated value or errors
changed. This is mostly used for optimization engines as an extension
of the `Access` behaviour.
The field will have changed if either its associated value, errors,
action, or implementation changed. This is mostly used for optimization
engines as an extension of the `Access` behaviour.
"""
@spec input_changed?(t, t, field()) :: 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,
%Form{
impl: impl1,
id: id1,
name: name1,
errors: errors1,
source: source1,
action: action1
} = form1,
%Form{
impl: impl2,
id: id2,
name: name2,
errors: errors2,
source: source2,
action: action2
} = form2,
field
)
when is_atom(field) or is_binary(field) do
impl1 != impl2 or id1 != id2 or name1 != name2 or
impl1 != impl2 or id1 != id2 or name1 != name2 or action1 != action2 or
field_errors(errors1, field) != field_errors(errors2, field) or
impl1.input_value(source1, form1, field) != impl2.input_value(source2, form2, field)
end
Expand Down
7 changes: 7 additions & 0 deletions lib/phoenix_html/form_data.ex
Expand Up @@ -56,6 +56,8 @@ defprotocol Phoenix.HTML.FormData do
applies if the field value is a list and no parameters were
sent through the form.
* `:action` - The user defined action being taken by the form, such
as `:validate`, `:save`, etc.
"""
@spec to_form(t, Phoenix.HTML.Form.t(), Phoenix.HTML.Form.field(), Keyword.t()) ::
[Phoenix.HTML.Form.t()]
Expand All @@ -79,6 +81,7 @@ defimpl Phoenix.HTML.FormData, for: Map do
def to_form(conn_or_atom_or_map, opts) do
{name, params, opts} = name_params_and_opts(conn_or_atom_or_map, opts)
{errors, opts} = Keyword.pop(opts, :errors, [])
{action, opts} = Keyword.pop(opts, :action, nil)
id = Keyword.get(opts, :id) || name

unless is_binary(id) or is_nil(id) do
Expand All @@ -93,6 +96,7 @@ defimpl Phoenix.HTML.FormData, for: Map do
params: params,
data: %{},
errors: errors,
action: action,
options: opts
}
end
Expand All @@ -118,6 +122,7 @@ defimpl Phoenix.HTML.FormData, for: Map do
{name, opts} = Keyword.pop(opts, :as)
{id, opts} = Keyword.pop(opts, :id)
{hidden, opts} = Keyword.pop(opts, :hidden, [])
{action, opts} = Keyword.pop(opts, :action)

id = to_string(id || form.id <> "_#{field}")
name = to_string(name || form.name <> "[#{field}]")
Expand All @@ -133,6 +138,7 @@ defimpl Phoenix.HTML.FormData, for: Map do
id: id,
name: name,
data: default,
action: action,
params: params || %{},
hidden: hidden,
options: opts
Expand All @@ -157,6 +163,7 @@ defimpl Phoenix.HTML.FormData, for: Map do
source: conn_or_atom_or_map,
impl: __MODULE__,
index: index,
action: action,
id: id <> "_" <> index_string,
name: name <> "[" <> index_string <> "]",
data: data,
Expand Down
6 changes: 6 additions & 0 deletions test/phoenix_html/form_test.exs
Expand Up @@ -138,6 +138,12 @@ defmodule Phoenix.HTML.FormTest do
assert input_changed?(form, form(%{"foo" => "bar"}), "foo")
end

test "input_changed? with changed action or method" do
form = form(%{}, action: :validate)
refute input_changed?(form, %{form | action: :validate}, :foo)
assert input_changed?(form, %{form | action: :save}, :foo)
end

describe "access" do
test "without name and atom keys" do
form =
Expand Down

0 comments on commit adf1c38

Please sign in to comment.