diff --git a/lib/phoenix_html/form.ex b/lib/phoenix_html/form.ex index 794252d..1d4ab67 100644 --- a/lib/phoenix_html/form.ex +++ b/lib/phoenix_html/form.ex @@ -64,6 +64,8 @@ defmodule Phoenix.HTML.Form do id: nil, name: nil, data: nil, + action: nil, + method: nil, hidden: [], params: %{}, errors: [], @@ -74,6 +76,8 @@ defmodule Phoenix.HTML.Form do source: Phoenix.HTML.FormData.t(), name: String.t(), data: %{field => term}, + action: nil | atom() | String.t(), + method: nil | atom() | String.t(), params: %{binary => term}, hidden: Keyword.t(), options: Keyword.t(), @@ -191,9 +195,9 @@ 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, method, 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?( @@ -202,7 +206,8 @@ defmodule Phoenix.HTML.Form do 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 form1.action != form2.action or + form1.method != form2.method or field_errors(errors1, field) != field_errors(errors2, field) or impl1.input_value(source1, form1, field) != impl2.input_value(source2, form2, field) end diff --git a/lib/phoenix_html/form_data.ex b/lib/phoenix_html/form_data.ex index 02eec75..afa5a6a 100644 --- a/lib/phoenix_html/form_data.ex +++ b/lib/phoenix_html/form_data.ex @@ -56,6 +56,10 @@ defprotocol Phoenix.HTML.FormData do applies if the field value is a list and no parameters were sent through the form. + * `:action` - The form action, such as the HTML `action` attribute + or LiveView action. + + * `:method` - The form method, such as the HTML `method` attribute. """ @spec to_form(t, Phoenix.HTML.Form.t(), Phoenix.HTML.Form.field(), Keyword.t()) :: [Phoenix.HTML.Form.t()] @@ -79,6 +83,8 @@ 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) + {method, opts} = Keyword.pop(opts, :method, nil) id = Keyword.get(opts, :id) || name unless is_binary(id) or is_nil(id) do @@ -93,6 +99,8 @@ defimpl Phoenix.HTML.FormData, for: Map do params: params, data: %{}, errors: errors, + action: action, + method: method, options: opts } end @@ -118,6 +126,8 @@ 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) + {method, opts} = Keyword.pop(opts, :method) id = to_string(id || form.id <> "_#{field}") name = to_string(name || form.name <> "[#{field}]") @@ -133,6 +143,8 @@ defimpl Phoenix.HTML.FormData, for: Map do id: id, name: name, data: default, + action: action, + method: method, params: params || %{}, hidden: hidden, options: opts @@ -157,6 +169,8 @@ defimpl Phoenix.HTML.FormData, for: Map do source: conn_or_atom_or_map, impl: __MODULE__, index: index, + action: action, + method: method, id: id <> "_" <> index_string, name: name <> "[" <> index_string <> "]", data: data, diff --git a/test/phoenix_html/form_test.exs b/test/phoenix_html/form_test.exs index cb11c45..bcc51ac 100644 --- a/test/phoenix_html/form_test.exs +++ b/test/phoenix_html/form_test.exs @@ -138,6 +138,13 @@ 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, method: "post") + refute input_changed?(form, %{form | action: :validate, method: "post"}, :foo) + assert input_changed?(form, %{form | action: :save}, :foo) + assert input_changed?(form, %{form | method: "put"}, :foo) + end + describe "access" do test "without name and atom keys" do form =