improvement: return phoenix forms if phoenix forms are given

this is done to support the new pattern in phoenix of assigning the html
form instead of the underlying data structure
This commit is contained in:
Zach Daniel 2023-03-05 17:23:43 -05:00
parent 673ae1bce8
commit 340af977f9
3 changed files with 114 additions and 39 deletions

View file

@ -444,8 +444,9 @@ defmodule MyAshPhoenixAppWeb.ExampleLiveView do
assign(socket,
posts: posts,
post_selector: post_selector(posts),
create_form: AshPhoenix.Form.for_create(Post, :create),
update_form: AshPhoenix.Form.for_update(List.first(posts, %Post{}), :update)
# the `to_form/1` calls below are for liveview 0.18.12+. For earlier versions, remove those calls
create_form: AshPhoenix.Form.for_create(Post, :create) |> to_form(),
update_form: AshPhoenix.Form.for_update(List.first(posts, %Post{}), :update) |> to_form()
)
{:ok, socket}

View file

@ -854,8 +854,15 @@ defmodule AshPhoenix.Form do
#{Spark.OptionsHelpers.docs(@validate_opts)}
"""
@spec validate(t(), map, Keyword.t()) :: t()
def validate(form, new_params, opts \\ []) do
form = require_form!(form)
@spec validate(Phoenix.HTML.Form.t(), map, Keyword.t()) :: Phoenix.HTML.Form.t()
def validate(form, new_params, opts \\ [])
def validate(%Phoenix.HTML.Form{} = form, new_params, opts) do
validate(form.source, new_params, opts)
|> Phoenix.HTML.FormData.to_form(form.options)
end
def validate(form, new_params, opts) do
opts = validate_opts_with_extra_keys(opts, @validate_opts)
prepare_source = form.prepare_source || (& &1)
@ -977,8 +984,15 @@ defmodule AshPhoenix.Form do
@doc """
Merge the new options with the saved options on a form. See `update_options/2` for more.
"""
@spec merge_options(t(), Keyword.t()) :: t()
@spec merge_options(Phoenix.HTML.Form.t(), Keyword.t()) :: Phoenix.HTML.Form.t()
def merge_options(%Phoenix.HTML.Form{} = form, opts) do
form.source
|> update_options(opts)
|> Phoenix.HTML.FormData.to_form(form.options)
end
def merge_options(form, opts) do
form = require_form!(form)
update_options(form, &Keyword.merge(&1, opts))
end
@ -990,8 +1004,13 @@ defmodule AshPhoenix.Form do
You may want to validate again after this has been changed if it can change the results of your form validation.
"""
def update_options(%Phoenix.HTML.Form{} = form, fun) do
form.source
|> update_options(fun)
|> Phoenix.HTML.FormData.to_form(form.options)
end
def update_options(form, fun) do
form = require_form!(form)
%{form | opts: fun.(form.opts)}
end
@ -1402,8 +1421,21 @@ defmodule AshPhoenix.Form do
| {:ok, Ash.Resource.record(), list(Ash.Notifier.Notification.t())}
| :ok
| {:error, t()}
def submit(form, opts \\ []) do
form = require_form!(form)
@spec submit(Phoenix.HTML.Form.t(), Keyword.t()) ::
{:ok, Ash.Resource.record() | nil | list(Ash.Notifier.Notification.t())}
| {:ok, Ash.Resource.record(), list(Ash.Notifier.Notification.t())}
| :ok
| {:error, Phoenix.HTML.Form.t()}
def submit(form, opts \\ [])
def submit(%Phoenix.HTML.Form{} = form, opts) do
form.source
|> submit(opts)
|> Phoenix.HTML.FormData.to_form(form.options)
end
def submit(form, opts) do
changeset_opts = Keyword.drop(form.opts, [:forms, :errors, :id, :method, :for, :as])
form =
@ -1593,8 +1625,6 @@ defmodule AshPhoenix.Form do
"""
@spec submit!(t(), Keyword.t()) :: Ash.Resource.record() | :ok | no_return
def submit!(form, opts \\ []) do
form = require_form!(form)
case submit(form, Keyword.put(opts, :raise?, true)) do
{:ok, value} ->
value
@ -1655,8 +1685,13 @@ defmodule AshPhoenix.Form do
end
end
def update_forms_at_path(%Phoenix.HTML.Form{} = form, path, func, opts) do
form.source
|> update_forms_at_path(path, func, opts)
|> Phoenix.HTML.FormData.to_form(form.options)
end
def update_forms_at_path(form, path, func, opts) do
form = require_form!(form)
opts = Spark.OptionsHelpers.validate!(opts, @update_form_opts)
path =
@ -1711,8 +1746,17 @@ defmodule AshPhoenix.Form do
This can be useful if you have a button that should modify a nested form in some way, for example.
"""
@spec update_form(t(), list(atom | integer) | String.t(), (t() -> t())) :: t()
def update_form(form, path, func, opts \\ []) do
form = require_form!(form)
@spec update_form(Phoenix.HTML.Form.t(), list(atom | integer) | String.t(), (t() -> t())) ::
Phoenix.HTML.Form.t()
def update_form(form, path, func, opts \\ [])
def update_form(%Phoenix.HTML.Form{} = form, path, func, opts) do
form.source
|> update_form(path, func, opts)
|> Phoenix.HTML.FormData.to_form(form.options)
end
def update_form(form, path, func, opts) do
opts = Spark.OptionsHelpers.validate!(opts, @update_form_opts)
path =
@ -1851,16 +1895,6 @@ defmodule AshPhoenix.Form do
"""
end
defp require_form!(%__MODULE__{} = form), do: form
defp require_form!(form) do
raise ArgumentError, """
Expected to receive an `%AshPhoenix.Form{}`.
Got: #{inspect(form)}
"""
end
defp add_index(form_params, index, opts) do
if opts[:sparse?] do
Map.put(form_params, "_index", to_string(index))
@ -2066,9 +2100,13 @@ defmodule AshPhoenix.Form do
Queries do not track data (because that wouldn't make sense), so this will not update the data
for read actions
"""
def set_data(form, data) do
form = require_form!(form)
def set_data(%Phoenix.HTML.Form{} = form, data) do
form.source
|> set_data(data)
|> Phoenix.HTML.FormData.to_form(form.options)
end
def set_data(form, data) do
case form.source do
%Ash.Changeset{} = source ->
%{form | data: data, source: %{source | data: data}}
@ -2084,12 +2122,17 @@ defmodule AshPhoenix.Form do
Accepts a field (atom) or a list of fields (atoms) as a second argument.
"""
@spec clear_value(t(), atom | [atom]) :: t()
def clear_value(%Phoenix.HTML.Form{} = form, field_or_fields) do
form.source
|> clear_value(field_or_fields)
|> Phoenix.HTML.FormData.to_form(form.options)
end
def clear_value(form, field_or_fields) when is_list(field_or_fields) do
Enum.reduce(field_or_fields, form, &clear_value(&2, &1))
end
def clear_value(form, field) do
form = require_form!(form)
string_and_atom = [field, to_string(field)]
common_dropped = %{
@ -2179,9 +2222,13 @@ defmodule AshPhoenix.Form do
to the string "true". Any other value will not result in the form being ignored.
"""
@spec ignore(t()) :: t()
def ignore(form) do
form = require_form!(form)
def ignore(%Phoenix.HTML.Form{} = form) do
form.source
|> ignore()
|> Phoenix.HTML.FormData.to_form(form.options)
end
def ignore(form) do
if ignored?(form) do
%{form | params: Map.delete(form.params, "_ignore")}
else
@ -2204,12 +2251,19 @@ defmodule AshPhoenix.Form do
This can be useful if you want to get the parameters and manipulate them/build a custom changeset
afterwards.
"""
@spec params(t() | Phoenix.HTML.Form.t()) :: map
@spec params(t()) :: map
def params(form, opts \\ []) do
form = to_form!(form)
# These options aren't documented because they are still experimental
hidden? = Keyword.get(opts, :hidden?, true)
excluded_empty_fields = Keyword.get(opts, :exclude_fields_if_empty, Keyword.get(form.opts, :exclude_fields_if_empty, []))
excluded_empty_fields =
Keyword.get(
opts,
:exclude_fields_if_empty,
Keyword.get(form.opts, :exclude_fields_if_empty, [])
)
indexer = opts[:indexer]
indexed_lists? = opts[:indexed_lists?] || not is_nil(indexer) || false
transform = opts[:transform]
@ -2259,7 +2313,13 @@ defmodule AshPhoenix.Form do
nested_form = form.forms[key]
if nested_form && filter.(nested_form) do
opts = Keyword.put(opts, :exclude_fields_if_empty, Keyword.get(excluded_empty_fields, key, []))
opts =
Keyword.put(
opts,
:exclude_fields_if_empty,
Keyword.get(excluded_empty_fields, key, [])
)
nested_params = params(nested_form, opts)
if nested_params["_ignore"] == "true" do
@ -2458,8 +2518,17 @@ defmodule AshPhoenix.Form do
#{Spark.OptionsHelpers.docs(@add_form_opts)}
"""
@spec add_form(t(), String.t() | atom | list(atom | integer), Keyword.t()) :: t()
def add_form(form, path, opts \\ []) do
form = require_form!(form)
@spec add_form(Phoenix.HTML.Form.t(), String.t() | atom | list(atom | integer), Keyword.t()) ::
Phoenix.HTML.Form.t()
def add_form(form, path, opts \\ [])
def add_form(%Phoenix.HTML.Form{} = form, path, opts) do
form.source
|> add_form(path, opts)
|> Phoenix.HTML.FormData.to_form(form.options)
end
def add_form(form, path, opts) do
opts = Spark.OptionsHelpers.validate!(opts, @add_form_opts)
form =
@ -2507,8 +2576,15 @@ defmodule AshPhoenix.Form do
#{Spark.OptionsHelpers.docs(@remove_form_opts)}
"""
def remove_form(form, path, opts \\ []) do
form = require_form!(form)
def remove_form(form, path, opts \\ [])
def remove_form(%Phoenix.HTML.Form{} = form, path, opts) do
form.source
|> remove_form(path, opts)
|> Phoenix.HTML.FormData.to_form(form.options)
end
def remove_form(form, path, opts) do
opts = Spark.OptionsHelpers.validate!(opts, @remove_form_opts)
if has_form?(form, path) do

View file

@ -233,14 +233,12 @@ defmodule AshPhoenix.FormTest do
AshPhoenix.Form.params(form)
end
test "a friendly error is provided if you use a phoenix form where you shouldn't have" do
test "a phoenix form is returned in cases where a phoenix form is passed in" do
form = Form.for_create(PostWithDefault, :create, api: Api)
form = AshPhoenix.Form.validate(form, %{"text" => ""}, errors: form.submitted_once?)
form = form_for(form, "foo")
assert_raise ArgumentError, ~r//, fn ->
AshPhoenix.Form.validate(form, %{})
end
assert %Phoenix.HTML.Form{} = AshPhoenix.Form.validate(form, %{})
end
test "it supports forms with data and a `type: :append_and_remove`" do