Athanor.Field (Athanor v0.1.0-beta.1)

Copy Markdown View Source

Behaviour for consumer-supplied :custom field modules.

A :custom field declared in Athanor.Component.fields/0 points at a module implementing this behaviour:

def fields, do: [
  {"image", :custom, label: "Image", module: MyApp.PageBuilder.Fields.MediaPicker}
]

The module IS a Phoenix.LiveComponent. Athanor mounts it inside the auto-generated configure panel with these assigns:

  • :value — current value of the prop (props[key]), or nil if unset
  • :on_change — 1-arity function. Call it with the new value and
               Athanor plumbs `:update_component_props` back to the
               host LiveView. Wholesale replace of `props[key]`.
  • :ctx — the full Athanor.Ctx. Use for passthrough context like
         `account_id`, `user_id`, `api_token`, etc.
  • :label — optional, declared via label: in the field opts.

The custom LC owns its own UI completely. It can mount more LCs inside, hit the DB, render whatever HTML it wants. Athanor stays out of the way.

Example

defmodule MyApp.PageBuilder.Fields.MediaPicker do
  use Phoenix.LiveComponent

  @behaviour Athanor.Field

  @impl true
  def update(assigns, socket) do
    {:ok, assign(socket, assigns)}
  end

  @impl true
  def render(assigns) do
    ~H"""
    <div>
      <img :if={@value} src={@value} class="..." />
      <button phx-click="pick" phx-target={@myself}>Pick image</button>
    </div>
    """
  end

  @impl true
  def handle_event("pick", _params, socket) do
    # Open media picker, get url back, then:
    socket.assigns.on_change.(url)
    {:noreply, socket}
  end
end

Why a LiveComponent and not a function component

Picker flows commonly need their own state (file uploads, async loads, multi-step modals). A Phoenix.LiveComponent lets the custom field own that state without leaking into the host LiveView.

Summary

Functions

Returns true when module implements the Athanor.Field behaviour (or at minimum exports the required LiveComponent callbacks).

Functions

implements?(module)

Returns true when module implements the Athanor.Field behaviour (or at minimum exports the required LiveComponent callbacks).