Corex.Form (Corex v0.1.0-alpha.33)

View Source

Helper functions to work with forms.

Corex form components such as Corex.Checkbox and Corex.Select with classic form and with Phoenix form using the Phoenix.Component.form/1 function.

Examples

Classic Form

In a classic form, you can use the name attribute to identify the checkbox. The generated parameters will be as follows: /?terms=true

This works in Controller View and Live View

<form id="my-form">
  <.checkbox name="terms" class="checkbox">
    <:label>I accept the terms</:label>
  </.checkbox>
  <button type="submit">Submit</button>
</form>

Phoenix Form

When using with Phoenix forms, you must add an id to the form using the Corex.Form.get_form_id/1 function.

Controller

def checkbox_form_page(conn, _params) do
  form =
    %MyApp.Form.Terms{}
    |> MyApp.Form.Terms.changeset(%{})
    |> Phoenix.Component.to_form(as: :terms, id: "checkbox-form")
  render(conn, :checkbox_form_page, form: form)
end
<.form :let={f} for={@form} id={Corex.Form.get_form_id(@form)} method="get">
<.checkbox field={f[:terms]} class="checkbox">
  <:label>I accept the terms</:label>
    <:error :let={msg}>
  <.heroicon name="hero-exclamation-circle" class="icon" />
  {msg}
</:error>
</.checkbox>
<button type="submit">Submit</button>
</.form>

In a Live View

Use Corex.Form.get_form_id/1 for the form id. For a form without Ecto validation you can build the form from a map:

def mount(_params, _session, socket) do
  form = to_form(%{"terms" => "false"}, as: :terms)
  {:ok, assign(socket, :form, form)}
end
<.form for={@form} id={Corex.Form.get_form_id(@form)}>
  <.checkbox field={@form[:terms]} class="checkbox">
    <:label>I accept the terms</:label>
    <:error :let={msg}>
      <.heroicon name="hero-exclamation-circle" class="icon" />
      {msg}
    </:error>
  </.checkbox>
  <button type="submit">Submit</button>
</.form>

With Ecto changeset

When using Ecto changeset for validation and inside a Live view you must enable the controlled mode.

This allows the Live View to be the source of truth and the component to be in sync accordingly

First create an embedded schema and changeset:

defmodule MyApp.Form.Terms do
  use Ecto.Schema
  import Ecto.Changeset

  embedded_schema do
    field :terms, :boolean, default: false
  end

  def changeset(terms, attrs \\ %{}) do
    terms
    |> cast(attrs, [:terms])
    |> validate_required([:terms])
    |> validate_acceptance(:terms)
  end
end
defmodule MyAppWeb.CheckboxFormLive do
  use MyAppWeb, :live_view
  alias MyApp.Form.Terms
  alias Corex.Form

  def mount(_params, _session, socket) do
    form = %Terms{} |> Terms.changeset(%{}) |> to_form(as: :terms)
    {:ok, assign(socket, :form, form)}
  end

  def handle_event("validate", %{"terms" => params}, socket) do
    changeset = Terms.changeset(%Terms{}, params)
    {:noreply, assign(socket, :form, to_form(changeset, action: :validate, as: :terms))}
  end

  def render(assigns) do
    ~H"""
    <.form for={@form} id={Form.get_form_id(@form)} phx-change="validate" phx-submit="save">
      <.checkbox field={@form[:terms]} class="checkbox" controlled>
      <:label>I accept the terms</:label>
      <:error :let={msg}>
        <.heroicon name="hero-exclamation-circle" class="icon" />
        {msg}
      </:error>
      </.checkbox>
    </.form>
    """
  end
end

Other Corex form components (e.g. Corex.Select, Corex.RadioGroup, Corex.Switch) follow the same pattern: use Corex.Form.get_form_id/1 on the form and pass field={@form[:field_name]} or field={f[:field_name]} with :let={f}.

Summary

Functions

Returns the form id.

Functions

get_form_id(changeset)

@spec get_form_id(Phoenix.HTML.Form.t() | Ecto.Changeset.t()) :: binary()

Returns the form id.

Accepts either:

Examples

When the form is built with Phoenix.Component.to_form/2, use the form assign:

<.form :let={f} for={@form} id={Corex.Form.get_form_id(@form)}>
  <.checkbox field={f[:terms]} class="checkbox">
    <:label>I accept the terms</:label>
  </.checkbox>
  <button type="submit">Submit</button>
</.form>

In a LiveView with controlled checkbox:

<.form for={@form} id={Corex.Form.get_form_id(@form)} phx-change="validate" phx-submit="save">
  <.checkbox field={@form[:terms]} class="checkbox" controlled>
    <:label>I accept the terms</:label>
  </.checkbox>
  <button type="submit">Submit</button>
</.form>