Doumi.Phoenix.Params - A helper library that supports converting form to params and params to form

Doumi.Phoenix.Params is a helper library that supports converting form to params and params to form.

It is still confusing to handle Ecto.Changeset, Phoenix.HTML.Form for form binding.

For example,

To make it easy and straightforward, I’ve made Doumi.Phoenix.Params.

defmodule MyAppWeb.TestParams do
  use Ecto.Schema
  import Ecto.Changeset

  @primary_key false
  embedded_schema do
    field :foo, :string
  end

  @required [:foo]
  def changeset(%__MODULE__{} = struct, attrs) do
    struct
    |> cast(attrs, @required)
    |> validate_required(@required)
  end
end

defmodule MyAppWeb.MyLive do
  use MyAppWeb, :live_view
  alias MyAppWeb.TestParams
  alias Doumi.Phoenix.Params

  ...

  @impl true
  def mount(_params, _session, socket) do
    # Without Doumi.Phoenix.Params
    # form =
    #   TestParams.changeset(%TestParams{}, %{})
    #   |> to_form(as: :test)

    # With Doumi.Phoenix.Params
    form = Params.to_form(%TestParams{}, %{}, as: :test, validate: false)

    socket = socket |> assign(:form, form)

    {:ok, socket}
  end

  @impl true
  def handle_event("validate", %{"test" => test_params}, socket) do
    # Without Doumi.Phoenix.Params
    # form =
    #   TestParams.changeset(%TestParams{}, test_params)
    #   |> to_form(as: :test)
    #   |> Map.put(:action, :validate)

    # With Doumi.Phoenix.Params
    form = Params.to_form(%TestParams{}, test_params, as: :test)

    socket = socket |> assign(:form, form)

    {:noreply, socket}
  end

  @impl true
  def handle_event("change_foo_from_outside_of_form", %{"foo" => foo}, socket) do
    # Without Doumi.Phoenix.Params
    # test_params =
    #   socket.assigns.form.source.params
    #   |> Map.put(%{"foo" => foo})
    #
    # form =
    #   TestParams.changeset(%TestParams{}, test_params)
    #   |> to_form(as: :test)
    #   |> Map.put(:action, :validate)

    # With Doumi.Phoenix.Params
    test_params =
      socket.assigns.form
      |> Params.to_params(%{"foo" => foo})

    form = Params.to_form(%TestParams{}, test_params, as: :test)

    socket = socket |> assign(:form, form)

    {:noreply, socket}
  end

  @impl true
  def handle_event("save", %{"test" => test_params}, socket) do
    # Without Doumi.Phoenix.Params
    # test_params_map =
    #   TestParams.changeset(%TestParams, test_params)
    #   |> Ecto.Changeset.apply_changes()
    #   |> some_how_change_nested_struct_to_map_if_you_use_map_with_atom_key()

    # With Doumi.Phoenix.Params
    test_params_map =
      Params.to_form(%TestParams{}, test_params)
      |> Params.to_map()

    :ok = TestDomain.create_test(test_params_map)

    {:noreplym, socket}
  end

  ...
end
4 Likes

v0.3.0 is released!

Now it provides use Doumi.Phoenix.Params, opts... that creates handy to_form/2 to the schema module.

defmodule MyAppWeb.TestParams do
  use Ecto.Schema
  use Doumi.Phoenix.Params, as: :test # <--- with use macro

  ...
end

defmodule MyAppWeb.MyLive do
  ...

  @impl true
  def handle_event("validate", %{"test" => test_params}, socket) do
    # Without Doumi.Phoenix.Params
    # form =
    #   TestParams.changeset(%TestParams{}, test_params)
    #   |> to_form(as: :test)
    #   |> Map.put(:action, :validate)

    # With Doumi.Phoenix.Params
    # form = Params.to_form(%TestParams{}, test_params, as: :test)

    # With use Doumi.Phoenix.Params
    form = TestParams.to_form(test_params) <--- much easier

    socket = socket |> assign(:form, form)

    {:noreply, socket}
  end
end

Thank you for reading it!

1 Like