# `Athanor.Field`
[🔗](https://github.com/Arsenalist/athanor/blob/v0.1.0-beta.7/lib/athanor/field.ex#L1)

Behaviour for consumer-supplied `:custom` field modules.

A `:custom` field declared in `c: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.

# `implements?`

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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
