La forma estándar de crear formularios en LiveView es utilizar el helper to_form/1 pasándole una estructura changeset, por ejemplo:

to_form(Accounts.change_user(%User{})))

Sin embargo, pueden surgir situaciones donde tengamos un formulario sin tener que respaldarlo con un changeset.

Porque saltarse el changeset?

Ecto.Changeset es una herramienta para trabajar con información de distintas fuentes. Proporciona validaciones, castings y manejo de errores. Normalmente creamos formularios basados en un schema de Ecto, es decir que tienen una tabla en nuestra base de datos. Sin embargo, hay situaciones en las que generar un changeset puede ser excesivo o innecesario, ya sea basado en un schema o en su forma schemaless.

Si simplemente queremos capturar datos sin necesidad de validaciones complejas, es posible que quieras evitar usar un changeset.

Vamos a poner como ejemplo un formulario de contacto (asumiendo que ya tienes un proyecto de Phoenix corriendo):

defmodule MyAppWeb.ContactLive do
  use MyAppWeb, :live_view

  @impl true
  def mount(_params, _session, socket) do
    params = %{"name" => "", "subject" => "", "body" => "", "email" => ""}
    {:ok, assign(socket, :form, to_form(params))}
  end

  @impl true
  def render(assigns) do
    ~H"""
    <div>
      <.header>
        <:subtitle>Contact</:subtitle>
      </.header>

      <.simple_form for={@form} id="contact_form" phx-submit="save">
        <.input field={@form[:name]} label="Name" />
        <.input field={@form[:subject]} label="Subject" />
        <.input field={@form[:body]} type="textarea" label="Body" />
        <.input field={@form[:email]} type="email" label="Email" />

        <:actions>
          <.button phx-disable-with="Saving...">Send</.button>
        </:actions>
      </.simple_form>
    </div>
    """
  end

  @impl true
  def handle_event("save", params, socket) do
    {:noreply, socket}
  end
end

Y en el router.ex:

 scope "/", MyAppWeb do
    pipe_through :browser

    get "/", PageController, :home

    live "/contact", ContactLive # <-- add this
  end

Si entramos a http://localhost:4000/contact tendremos nuestro formulario de contacto:

Formulario de contacto sin changeset en LiveView

Como vemos la única diferencia es que al helper to_form/1 le pasaremos un mapa en lugar de un changeset:

# with changeset
changeset = Accounts.change_user(%User{}))
to_form(changeset)

# without changeset
params = %{"name" => "", "subject" => "", "body" => "", "email" => ""}
to_form(params)

Si probamos el formulario podremos ver lo siguiente en los logs:

Logs recibiendo los parámetros del formulario

De esta forma podemos seguir aprovechando el poder de los helpers y los nuevos componentes del modulo CoreComponents.