PhoenixTypedForm

Hex.pm

A macro that enforces a typed schema for your Phoenix LiveView forms. The idea is that you define your schema and an optional changeset, then let the macro do the heavy lifting from there.

The macro provides a way to create a new form, update a form, and check if a form is valid for submission, all based on your schema and changeset.

A typed form will have the following properties:

The macro suports:

Example

Here's an example of how to use this module in your Phoenix app. Let's say you want a really simple form for a user to enter a quantity for a purchase order. You'd define a module like this for your form:

defmodule BasicForm do
  use PhoenixTypedForm

  # Define the schema for the form
  typed_schema do
    field :qty, :integer
  end

  # Bring in the form helpers
  def_typed_form()

  # And finally render the form to the user
  def render_form(assigns) do
    # Some things to note:
    # 1) phx-submit also works instead of phx-change
    # 2) `value={@form.data.qty}` - This will show the last value that's been validated by the changeset
    #     You can also use `value={@form.params.qty}` to show the last value that's been submitted instead
    ~H"""
    <.form for={@form} phx-change="update-form">
      <p>Enter qty</p>
      <.input
        field={@form[:qty]}
        type="number"
        step="1.0"
        label="Qty"
        min="0"
        value={@form.data.qty}
        class={@class}
        errors={get_error(@form, :qty)}
        required
      />
    </.form>
    """
  end
end

And then in your LiveView, all you have to do is

  1. create a new form and put it in your socket assigns
  2. handle the event fired by the form

If the new details are invalid, the form can render the changeset error straight to the user, giving them feedback as early as possible. This macro could also be applied to forms in a controller, but it was intended to be used with LiveView.

defmodule YourLiveView do
  use Phoenix.LiveView

  def mount(_params, _session, socket) do
    {:ok, assign(socket, form: BasicForm.new_form())}
  end

  def handle_event("update-form", %{"form" => form_params}, socket) do
    {:noreply, assign(socket, form: BasicForm.update_form(form_params))}
  end
end

To add a form in your heex template, it's really simple. Just invoke the render_form function component:

<%= BasicForm.render_form form={@form} %>

Installation

If available in Hex, the package can be installed by adding phoenix_typed_form to your list of dependencies in mix.exs:

def deps do
  [
    {:phoenix_typed_form, "~> 0.1.0"}
  ]
end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/phoenix_typed_form.